blob: 3cb6a3fe03cedc962887add23ed166cccc0aed4f [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>
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +020015#include <sdbusplus/asio/property.hpp>
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080016#include <sdbusplus/bus/match.hpp>
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +020017#include <sdbusplus/unpack_properties.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050018
George Liu7a1dbc42022-12-07 16:03:22 +080019#include <array>
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080020#include <memory>
George Liu7a1dbc42022-12-07 16:03:22 +080021#include <string_view>
22
Marri Devender Rao5968cae2019-01-21 10:27:12 -060023namespace redfish
24{
25namespace certs
26{
Patrick Williams89492a12023-05-10 07:51:34 -050027constexpr const char* certInstallIntf = "xyz.openbmc_project.Certs.Install";
28constexpr const char* certReplaceIntf = "xyz.openbmc_project.Certs.Replace";
29constexpr const char* objDeleteIntf = "xyz.openbmc_project.Object.Delete";
30constexpr const char* certPropIntf = "xyz.openbmc_project.Certs.Certificate";
31constexpr const char* dbusPropIntf = "org.freedesktop.DBus.Properties";
32constexpr const char* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
33constexpr const char* httpsServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060034 "xyz.openbmc_project.Certs.Manager.Server.Https";
Patrick Williams89492a12023-05-10 07:51:34 -050035constexpr const char* ldapServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060036 "xyz.openbmc_project.Certs.Manager.Client.Ldap";
Patrick Williams89492a12023-05-10 07:51:34 -050037constexpr const char* authorityServiceName =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050038 "xyz.openbmc_project.Certs.Manager.Authority.Ldap";
Patrick Williams89492a12023-05-10 07:51:34 -050039constexpr const char* baseObjectPath = "/xyz/openbmc_project/certs";
40constexpr const char* httpsObjectPath =
Jiaqing Zhaoc6a8dfb2022-06-03 10:44:23 +080041 "/xyz/openbmc_project/certs/server/https";
Patrick Williams89492a12023-05-10 07:51:34 -050042constexpr const char* ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap";
43constexpr const char* authorityObjectPath =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050044 "/xyz/openbmc_project/certs/authority/ldap";
Marri Devender Rao5968cae2019-01-21 10:27:12 -060045} // namespace certs
46
47/**
48 * The Certificate schema defines a Certificate Service which represents the
49 * actions available to manage certificates and links to where certificates
50 * are installed.
51 */
Marri Devender Rao5968cae2019-01-21 10:27:12 -060052
zhanghch058d1b46d2021-04-01 11:18:24 +080053inline std::string getCertificateFromReqBody(
54 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
55 const crow::Request& req)
Kowalski, Kamil58eb2382019-08-12 11:54:31 +020056{
Ed Tanous1aa0c2b2022-02-08 12:24:30 +010057 nlohmann::json reqJson;
58 JsonParseResult ret = parseRequestAsJson(req, reqJson);
59 if (ret != JsonParseResult::Success)
Kowalski, Kamil58eb2382019-08-12 11:54:31 +020060 {
61 // We did not receive JSON request, proceed as it is RAW data
Ed Tanous33c6b582023-02-14 15:05:48 -080062 return req.body();
Kowalski, Kamil58eb2382019-08-12 11:54:31 +020063 }
64
65 std::string certificate;
66 std::optional<std::string> certificateType = "PEM";
67
Willy Tu15ed6782021-12-14 11:03:16 -080068 if (!json_util::readJsonPatch(req, asyncResp->res, "CertificateString",
69 certificate, "CertificateType",
70 certificateType))
Kowalski, Kamil58eb2382019-08-12 11:54:31 +020071 {
72 BMCWEB_LOG_ERROR << "Required parameters are missing";
73 messages::internalError(asyncResp->res);
Ed Tanousabb93cd2021-09-02 14:34:57 -070074 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +020075 }
76
77 if (*certificateType != "PEM")
78 {
79 messages::propertyValueNotInList(asyncResp->res, *certificateType,
80 "CertificateType");
Ed Tanousabb93cd2021-09-02 14:34:57 -070081 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +020082 }
83
84 return certificate;
85}
86
Marri Devender Rao5968cae2019-01-21 10:27:12 -060087/**
88 * Class to create a temporary certificate file for uploading to system
89 */
90class CertificateFile
91{
92 public:
93 CertificateFile() = delete;
Gunnar Mills1214b7e2020-06-04 10:11:30 -050094 CertificateFile(const CertificateFile&) = delete;
95 CertificateFile& operator=(const CertificateFile&) = delete;
96 CertificateFile(CertificateFile&&) = delete;
97 CertificateFile& operator=(CertificateFile&&) = delete;
Ed Tanous4e23a442022-06-06 09:57:26 -070098 explicit CertificateFile(const std::string& certString)
Marri Devender Rao5968cae2019-01-21 10:27:12 -060099 {
Ed Tanous72d52d22020-10-12 07:46:27 -0700100 std::array<char, 18> dirTemplate = {'/', 't', 'm', 'p', '/', 'C',
Ed Tanous52074382020-09-28 19:16:18 -0700101 'e', 'r', 't', 's', '.', 'X',
102 'X', 'X', 'X', 'X', 'X', '\0'};
103 char* tempDirectory = mkdtemp(dirTemplate.data());
Ed Tanouse662eae2022-01-25 10:39:19 -0800104 if (tempDirectory != nullptr)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600105 {
106 certDirectory = tempDirectory;
107 certificateFile = certDirectory / "cert.pem";
108 std::ofstream out(certificateFile, std::ofstream::out |
109 std::ofstream::binary |
110 std::ofstream::trunc);
111 out << certString;
112 out.close();
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800113 BMCWEB_LOG_DEBUG << "Creating certificate file"
114 << certificateFile.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600115 }
116 }
117 ~CertificateFile()
118 {
119 if (std::filesystem::exists(certDirectory))
120 {
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800121 BMCWEB_LOG_DEBUG << "Removing certificate file"
122 << certificateFile.string();
Ed Tanous23a21a12020-07-25 04:45:05 +0000123 std::error_code ec;
124 std::filesystem::remove_all(certDirectory, ec);
125 if (ec)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600126 {
127 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800128 << certDirectory.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600129 }
130 }
131 }
132 std::string getCertFilePath()
133 {
134 return certificateFile;
135 }
136
137 private:
138 std::filesystem::path certificateFile;
139 std::filesystem::path certDirectory;
140};
141
142/**
Gunnar Mills4e0453b2020-07-08 14:00:30 -0500143 * @brief Parse and update Certificate Issue/Subject property
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600144 *
145 * @param[in] asyncResp Shared pointer to the response message
146 * @param[in] str Issuer/Subject value in key=value pairs
147 * @param[in] type Issuer/Subject
148 * @return None
149 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500150static void updateCertIssuerOrSubject(nlohmann::json& out,
Ed Tanous26ccae32023-02-16 10:28:44 -0800151 std::string_view value)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600152{
153 // example: O=openbmc-project.xyz,CN=localhost
154 std::string_view::iterator i = value.begin();
155 while (i != value.end())
156 {
157 std::string_view::iterator tokenBegin = i;
158 while (i != value.end() && *i != '=')
159 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530160 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600161 }
162 if (i == value.end())
163 {
164 break;
165 }
Ed Tanous26ccae32023-02-16 10:28:44 -0800166 std::string_view key(tokenBegin, static_cast<size_t>(i - tokenBegin));
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530167 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600168 tokenBegin = i;
169 while (i != value.end() && *i != ',')
170 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530171 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600172 }
Ed Tanous26ccae32023-02-16 10:28:44 -0800173 std::string_view val(tokenBegin, static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600174 if (key == "L")
175 {
176 out["City"] = val;
177 }
178 else if (key == "CN")
179 {
180 out["CommonName"] = val;
181 }
182 else if (key == "C")
183 {
184 out["Country"] = val;
185 }
186 else if (key == "O")
187 {
188 out["Organization"] = val;
189 }
190 else if (key == "OU")
191 {
192 out["OrganizationalUnit"] = val;
193 }
194 else if (key == "ST")
195 {
196 out["State"] = val;
197 }
198 // skip comma character
199 if (i != value.end())
200 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530201 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600202 }
203 }
204}
205
206/**
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800207 * @brief Retrieve the installed certificate list
208 *
209 * @param[in] asyncResp Shared pointer to the response message
210 * @param[in] basePath DBus object path to search
211 * @param[in] listPtr Json pointer to the list in asyncResp
212 * @param[in] countPtr Json pointer to the count in asyncResp
213 * @return None
214 */
215static void
216 getCertificateList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
217 const std::string& basePath,
218 const nlohmann::json::json_pointer& listPtr,
219 const nlohmann::json::json_pointer& countPtr)
220{
George Liu7a1dbc42022-12-07 16:03:22 +0800221 constexpr std::array<std::string_view, 1> interfaces = {
222 certs::certPropIntf};
223 dbus::utility::getSubTreePaths(
224 basePath, 0, interfaces,
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800225 [asyncResp, listPtr, countPtr](
George Liu7a1dbc42022-12-07 16:03:22 +0800226 const boost::system::error_code& ec,
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800227 const dbus::utility::MapperGetSubTreePathsResponse& certPaths) {
228 if (ec)
229 {
230 BMCWEB_LOG_ERROR << "Certificate collection query failed: " << ec;
231 messages::internalError(asyncResp->res);
232 return;
233 }
234
235 nlohmann::json& links = asyncResp->res.jsonValue[listPtr];
236 links = nlohmann::json::array();
237 for (const auto& certPath : certPaths)
238 {
239 sdbusplus::message::object_path objPath(certPath);
240 std::string certId = objPath.filename();
241 if (certId.empty())
242 {
243 BMCWEB_LOG_ERROR << "Invalid certificate objPath " << certPath;
244 continue;
245 }
246
247 boost::urls::url certURL;
248 if (objPath.parent_path() == certs::httpsObjectPath)
249 {
250 certURL = crow::utility::urlFromPieces(
251 "redfish", "v1", "Managers", "bmc", "NetworkProtocol",
252 "HTTPS", "Certificates", certId);
253 }
254 else if (objPath.parent_path() == certs::ldapObjectPath)
255 {
256 certURL = crow::utility::urlFromPieces("redfish", "v1",
257 "AccountService", "LDAP",
258 "Certificates", certId);
259 }
260 else if (objPath.parent_path() == certs::authorityObjectPath)
261 {
262 certURL = crow::utility::urlFromPieces(
263 "redfish", "v1", "Managers", "bmc", "Truststore",
264 "Certificates", certId);
265 }
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;
430 allowed.push_back("PEM");
431 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";
477 messages::internalError(asyncResp->res);
478 return;
479 }
480
481 if (!certificateType)
482 {
483 // should never happen, but it never hurts to be paranoid.
484 return;
485 }
486 if (certificateType != "PEM")
487 {
488 messages::actionParameterNotSupported(asyncResp->res, "CertificateType",
489 "ReplaceCertificate");
490 return;
491 }
492
493 std::string certURI;
494 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
495 "@odata.id", certURI))
496 {
497 messages::actionParameterMissing(asyncResp->res, "ReplaceCertificate",
498 "CertificateUri");
499 return;
500 }
501 BMCWEB_LOG_INFO << "Certificate URI to replace: " << certURI;
502
503 boost::urls::result<boost::urls::url_view> parsedUrl =
504 boost::urls::parse_relative_ref(certURI);
505 if (!parsedUrl)
506 {
507 messages::actionParameterValueFormatError(
508 asyncResp->res, certURI, "CertificateUri", "ReplaceCertificate");
509 return;
510 }
511
512 std::string id;
513 sdbusplus::message::object_path objectPath;
514 std::string name;
515 std::string service;
516 if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1", "Managers",
517 "bmc", "NetworkProtocol", "HTTPS",
518 "Certificates", std::ref(id)))
519 {
Patrick Williams89492a12023-05-10 07:51:34 -0500520 objectPath = sdbusplus::message::object_path(certs::httpsObjectPath) /
521 id;
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800522 name = "HTTPS certificate";
523 service = certs::httpsServiceName;
524 }
525 else if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1",
526 "AccountService", "LDAP",
527 "Certificates", std::ref(id)))
528 {
Patrick Williams89492a12023-05-10 07:51:34 -0500529 objectPath = sdbusplus::message::object_path(certs::ldapObjectPath) /
530 id;
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800531 name = "LDAP certificate";
532 service = certs::ldapServiceName;
533 }
534 else if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1",
535 "Managers", "bmc", "Truststore",
536 "Certificates", std::ref(id)))
537 {
538 objectPath =
539 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
540 name = "TrustStore certificate";
541 service = certs::authorityServiceName;
542 }
543 else
544 {
545 messages::actionParameterNotSupported(asyncResp->res, "CertificateUri",
546 "ReplaceCertificate");
547 return;
548 }
549
550 std::shared_ptr<CertificateFile> certFile =
551 std::make_shared<CertificateFile>(certificate);
552 crow::connections::systemBus->async_method_call(
553 [asyncResp, certFile, objectPath, service, url{*parsedUrl}, id,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800554 name](const boost::system::error_code& ec) {
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800555 if (ec)
556 {
557 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
558 if (ec.value() ==
559 boost::system::linux_error::bad_request_descriptor)
560 {
561 messages::resourceNotFound(asyncResp->res, "Certificate", id);
562 return;
563 }
564 messages::internalError(asyncResp->res);
565 return;
566 }
567 getCertificateProperties(asyncResp, objectPath, service, id, url, name);
568 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
569 << certFile->getCertFilePath();
570 },
571 service, objectPath, certs::certReplaceIntf, "Replace",
572 certFile->getCertFilePath());
573}
574
Ed Tanouscf9e4172022-12-21 09:30:16 -0800575// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800576static std::unique_ptr<sdbusplus::bus::match_t> csrMatcher;
577/**
578 * @brief Read data from CSR D-bus object and set to response
579 *
580 * @param[in] asyncResp Shared pointer to the response message
581 * @param[in] certURI Link to certifiate collection URI
582 * @param[in] service D-Bus service name
583 * @param[in] certObjPath certificate D-Bus object path
584 * @param[in] csrObjPath CSR D-Bus object path
585 * @return None
586 */
587static void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
588 const std::string& certURI, const std::string& service,
589 const std::string& certObjPath,
590 const std::string& csrObjPath)
591{
592 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
593 << " CSRObjectPath=" << csrObjPath
594 << " service=" << service;
595 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800596 [asyncResp, certURI](const boost::system::error_code& ec,
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800597 const std::string& csr) {
598 if (ec)
599 {
600 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
601 messages::internalError(asyncResp->res);
602 return;
603 }
604 if (csr.empty())
605 {
606 BMCWEB_LOG_ERROR << "CSR read is empty";
607 messages::internalError(asyncResp->res);
608 return;
609 }
610 asyncResp->res.jsonValue["CSRString"] = csr;
611 asyncResp->res.jsonValue["CertificateCollection"]["@odata.id"] =
612 certURI;
613 },
614 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
615}
616
617inline void
618 handleGenerateCSRAction(App& app, const crow::Request& req,
619 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
620{
621 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
622 {
623 return;
624 }
625 static const int rsaKeyBitLength = 2048;
626
627 // Required parameters
628 std::string city;
629 std::string commonName;
630 std::string country;
631 std::string organization;
632 std::string organizationalUnit;
633 std::string state;
634 nlohmann::json certificateCollection;
635
636 // Optional parameters
637 std::optional<std::vector<std::string>> optAlternativeNames =
638 std::vector<std::string>();
639 std::optional<std::string> optContactPerson = "";
640 std::optional<std::string> optChallengePassword = "";
641 std::optional<std::string> optEmail = "";
642 std::optional<std::string> optGivenName = "";
643 std::optional<std::string> optInitials = "";
644 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength;
645 std::optional<std::string> optKeyCurveId = "secp384r1";
646 std::optional<std::string> optKeyPairAlgorithm = "EC";
647 std::optional<std::vector<std::string>> optKeyUsage =
648 std::vector<std::string>();
649 std::optional<std::string> optSurname = "";
650 std::optional<std::string> optUnstructuredName = "";
651 if (!json_util::readJsonAction(
652 req, asyncResp->res, "City", city, "CommonName", commonName,
653 "ContactPerson", optContactPerson, "Country", country,
654 "Organization", organization, "OrganizationalUnit",
655 organizationalUnit, "State", state, "CertificateCollection",
656 certificateCollection, "AlternativeNames", optAlternativeNames,
657 "ChallengePassword", optChallengePassword, "Email", optEmail,
658 "GivenName", optGivenName, "Initials", optInitials, "KeyBitLength",
659 optKeyBitLength, "KeyCurveId", optKeyCurveId, "KeyPairAlgorithm",
660 optKeyPairAlgorithm, "KeyUsage", optKeyUsage, "Surname", optSurname,
661 "UnstructuredName", optUnstructuredName))
662 {
663 return;
664 }
665
666 // bmcweb has no way to store or decode a private key challenge
667 // password, which will likely cause bmcweb to crash on startup
668 // if this is not set on a post so not allowing the user to set
669 // value
670 if (!optChallengePassword->empty())
671 {
672 messages::actionParameterNotSupported(asyncResp->res, "GenerateCSR",
673 "ChallengePassword");
674 return;
675 }
676
677 std::string certURI;
678 if (!redfish::json_util::readJson(certificateCollection, asyncResp->res,
679 "@odata.id", certURI))
680 {
681 return;
682 }
683
684 std::string objectPath;
685 std::string service;
686 if (certURI.starts_with(
687 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
688 {
689 objectPath = certs::httpsObjectPath;
690 service = certs::httpsServiceName;
691 }
692 else if (certURI.starts_with(
693 "/redfish/v1/AccountService/LDAP/Certificates"))
694 {
695 objectPath = certs::ldapObjectPath;
696 service = certs::ldapServiceName;
697 }
698 else
699 {
700 messages::actionParameterNotSupported(
701 asyncResp->res, "CertificateCollection", "GenerateCSR");
702 return;
703 }
704
705 // supporting only EC and RSA algorithm
706 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA")
707 {
708 messages::actionParameterNotSupported(
709 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
710 return;
711 }
712
713 // supporting only 2048 key bit length for RSA algorithm due to
714 // time consumed in generating private key
715 if (*optKeyPairAlgorithm == "RSA" && *optKeyBitLength != rsaKeyBitLength)
716 {
717 messages::propertyValueNotInList(
718 asyncResp->res, std::to_string(*optKeyBitLength), "KeyBitLength");
719 return;
720 }
721
722 // validate KeyUsage supporting only 1 type based on URL
723 if (certURI.starts_with(
724 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
725 {
726 if (optKeyUsage->empty())
727 {
728 optKeyUsage->push_back("ServerAuthentication");
729 }
730 else if (optKeyUsage->size() == 1)
731 {
732 if ((*optKeyUsage)[0] != "ServerAuthentication")
733 {
734 messages::propertyValueNotInList(asyncResp->res,
735 (*optKeyUsage)[0], "KeyUsage");
736 return;
737 }
738 }
739 else
740 {
741 messages::actionParameterNotSupported(asyncResp->res, "KeyUsage",
742 "GenerateCSR");
743 return;
744 }
745 }
746 else if (certURI.starts_with(
747 "/redfish/v1/AccountService/LDAP/Certificates"))
748 {
749 if (optKeyUsage->empty())
750 {
751 optKeyUsage->push_back("ClientAuthentication");
752 }
753 else if (optKeyUsage->size() == 1)
754 {
755 if ((*optKeyUsage)[0] != "ClientAuthentication")
756 {
757 messages::propertyValueNotInList(asyncResp->res,
758 (*optKeyUsage)[0], "KeyUsage");
759 return;
760 }
761 }
762 else
763 {
764 messages::actionParameterNotSupported(asyncResp->res, "KeyUsage",
765 "GenerateCSR");
766 return;
767 }
768 }
769
770 // Only allow one CSR matcher at a time so setting retry
771 // time-out and timer expiry to 10 seconds for now.
772 static const int timeOut = 10;
773 if (csrMatcher)
774 {
775 messages::serviceTemporarilyUnavailable(asyncResp->res,
776 std::to_string(timeOut));
777 return;
778 }
779
780 // Make this static so it survives outside this method
781 static boost::asio::steady_timer timeout(*req.ioService);
782 timeout.expires_after(std::chrono::seconds(timeOut));
783 timeout.async_wait([asyncResp](const boost::system::error_code& ec) {
784 csrMatcher = nullptr;
785 if (ec)
786 {
787 // operation_aborted is expected if timer is canceled
788 // before completion.
789 if (ec != boost::asio::error::operation_aborted)
790 {
791 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
792 }
793 return;
794 }
795 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
796 messages::internalError(asyncResp->res);
797 });
798
799 // create a matcher to wait on CSR object
800 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
801 std::string match("type='signal',"
802 "interface='org.freedesktop.DBus.ObjectManager',"
803 "path='" +
804 objectPath +
805 "',"
806 "member='InterfacesAdded'");
807 csrMatcher = std::make_unique<sdbusplus::bus::match_t>(
808 *crow::connections::systemBus, match,
809 [asyncResp, service, objectPath, certURI](sdbusplus::message_t& m) {
810 timeout.cancel();
811 if (m.is_method_error())
812 {
813 BMCWEB_LOG_ERROR << "Dbus method error!!!";
814 messages::internalError(asyncResp->res);
815 return;
816 }
817
818 dbus::utility::DBusInteracesMap interfacesProperties;
819
820 sdbusplus::message::object_path csrObjectPath;
821 m.read(csrObjectPath, interfacesProperties);
822 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
823 for (const auto& interface : interfacesProperties)
824 {
825 if (interface.first == "xyz.openbmc_project.Certs.CSR")
826 {
827 getCSR(asyncResp, certURI, service, objectPath,
828 csrObjectPath.str);
829 break;
830 }
831 }
832 });
833 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800834 [asyncResp](const boost::system::error_code& ec, const std::string&) {
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800835 if (ec)
836 {
837 BMCWEB_LOG_ERROR << "DBUS response error: " << ec.message();
838 messages::internalError(asyncResp->res);
839 return;
840 }
841 },
842 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
843 "GenerateCSR", *optAlternativeNames, *optChallengePassword, city,
844 commonName, *optContactPerson, country, *optEmail, *optGivenName,
845 *optInitials, *optKeyBitLength, *optKeyCurveId, *optKeyPairAlgorithm,
846 *optKeyUsage, organization, organizationalUnit, state, *optSurname,
847 *optUnstructuredName);
848}
849
850inline void requestRoutesCertificateService(App& app)
851{
852 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/")
853 .privileges(redfish::privileges::getCertificateService)
854 .methods(boost::beast::http::verb::get)(
855 std::bind_front(handleCertificateServiceGet, std::ref(app)));
856
857 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
858 .privileges(redfish::privileges::getCertificateLocations)
859 .methods(boost::beast::http::verb::get)(
860 std::bind_front(handleCertificateLocationsGet, std::ref(app)));
861
George Liu0fda0f12021-11-16 10:06:17 +0800862 BMCWEB_ROUTE(
863 app,
864 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate/")
Ed Tanoused398212021-06-09 17:05:54 -0700865 .privileges(redfish::privileges::postCertificateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700866 .methods(boost::beast::http::verb::post)(
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800867 std::bind_front(handleReplaceCertificateAction, std::ref(app)));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600868
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800869 BMCWEB_ROUTE(
870 app,
871 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR/")
872 .privileges(redfish::privileges::postCertificateService)
873 .methods(boost::beast::http::verb::post)(
874 std::bind_front(handleGenerateCSRAction, std::ref(app)));
875} // requestRoutesCertificateService
876
877inline void handleHTTPSCertificateCollectionGet(
878 App& app, const crow::Request& req,
879 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
880{
881 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
882 {
883 return;
884 }
885
886 asyncResp->res.jsonValue["@odata.id"] =
887 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates";
888 asyncResp->res.jsonValue["@odata.type"] =
889 "#CertificateCollection.CertificateCollection";
890 asyncResp->res.jsonValue["Name"] = "HTTPS Certificates Collection";
891 asyncResp->res.jsonValue["Description"] =
892 "A Collection of HTTPS certificate instances";
893
894 getCertificateList(asyncResp, certs::httpsObjectPath,
895 "/Members"_json_pointer,
896 "/Members@odata.count"_json_pointer);
897}
898
899inline void handleHTTPSCertificateCollectionPost(
900 App& app, const crow::Request& req,
901 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
902{
903 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
904 {
905 return;
906 }
907 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
908
909 asyncResp->res.jsonValue["Name"] = "HTTPS Certificate";
910 asyncResp->res.jsonValue["Description"] = "HTTPS Certificate";
911
912 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
913
914 if (certFileBody.empty())
915 {
916 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
917 messages::unrecognizedRequestBody(asyncResp->res);
918 return;
919 }
920
921 std::shared_ptr<CertificateFile> certFile =
922 std::make_shared<CertificateFile>(certFileBody);
923
924 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800925 [asyncResp, certFile](const boost::system::error_code& ec,
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800926 const std::string& objectPath) {
927 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -0700928 {
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800929 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Ed Tanous002d39b2022-05-31 08:59:27 -0700930 messages::internalError(asyncResp->res);
931 return;
932 }
933
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800934 sdbusplus::message::object_path path(objectPath);
935 std::string certId = path.filename();
936 const boost::urls::url certURL = crow::utility::urlFromPieces(
937 "redfish", "v1", "Managers", "bmc", "NetworkProtocol", "HTTPS",
938 "Certificates", certId);
939 getCertificateProperties(asyncResp, objectPath, certs::httpsServiceName,
940 certId, certURL, "HTTPS Certificate");
941 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
942 << certFile->getCertFilePath();
943 },
944 certs::httpsServiceName, certs::httpsObjectPath, certs::certInstallIntf,
945 "Install", certFile->getCertFilePath());
946}
Ed Tanous002d39b2022-05-31 08:59:27 -0700947
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800948inline void handleHTTPSCertificateGet(
949 App& app, const crow::Request& req,
950 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
951{
952 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
953 {
954 return;
955 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700956
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800957 BMCWEB_LOG_DEBUG << "HTTPS Certificate ID=" << id;
958 const boost::urls::url certURL = crow::utility::urlFromPieces(
959 "redfish", "v1", "Managers", "bmc", "NetworkProtocol", "HTTPS",
960 "Certificates", id);
961 std::string objPath =
962 sdbusplus::message::object_path(certs::httpsObjectPath) / id;
963 getCertificateProperties(asyncResp, objPath, certs::httpsServiceName, id,
964 certURL, "HTTPS Certificate");
965}
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700966
967inline void requestRoutesHTTPSCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600968{
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800969 BMCWEB_ROUTE(app,
970 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
971 .privileges(redfish::privileges::getCertificateCollection)
972 .methods(boost::beast::http::verb::get)(std::bind_front(
973 handleHTTPSCertificateCollectionGet, std::ref(app)));
974
975 BMCWEB_ROUTE(app,
976 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
977 .privileges(redfish::privileges::postCertificateCollection)
978 .methods(boost::beast::http::verb::post)(std::bind_front(
979 handleHTTPSCertificateCollectionPost, std::ref(app)));
980
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700981 BMCWEB_ROUTE(
982 app,
983 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700984 .privileges(redfish::privileges::getCertificate)
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800985 .methods(boost::beast::http::verb::get)(
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800986 std::bind_front(handleHTTPSCertificateGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700987}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600988
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800989inline void handleLDAPCertificateCollectionGet(
990 App& app, const crow::Request& req,
991 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600992{
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800993 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
994 {
995 return;
996 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700997
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800998 asyncResp->res.jsonValue["@odata.id"] =
999 "/redfish/v1/AccountService/LDAP/Certificates";
1000 asyncResp->res.jsonValue["@odata.type"] =
1001 "#CertificateCollection.CertificateCollection";
1002 asyncResp->res.jsonValue["Name"] = "LDAP Certificates Collection";
1003 asyncResp->res.jsonValue["Description"] =
1004 "A Collection of LDAP certificate instances";
Ed Tanous002d39b2022-05-31 08:59:27 -07001005
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001006 getCertificateList(asyncResp, certs::ldapObjectPath,
1007 "/Members"_json_pointer,
1008 "/Members@odata.count"_json_pointer);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001009}
Marri Devender Rao37cce912019-02-20 01:05:22 -06001010
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001011inline void handleLDAPCertificateCollectionPost(
1012 App& app, const crow::Request& req,
1013 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001014{
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001015 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1016 {
1017 return;
1018 }
1019 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1020
1021 if (certFileBody.empty())
1022 {
1023 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1024 messages::unrecognizedRequestBody(asyncResp->res);
1025 return;
1026 }
1027
1028 std::shared_ptr<CertificateFile> certFile =
1029 std::make_shared<CertificateFile>(certFileBody);
1030
1031 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001032 [asyncResp, certFile](const boost::system::error_code& ec,
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001033 const std::string& objectPath) {
1034 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -07001035 {
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001036 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1037 messages::internalError(asyncResp->res);
Ed Tanous002d39b2022-05-31 08:59:27 -07001038 return;
1039 }
1040
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001041 sdbusplus::message::object_path path(objectPath);
1042 std::string certId = path.filename();
1043 const boost::urls::url certURL = crow::utility::urlFromPieces(
1044 "redfish", "v1", "AccountService", "LDAP", "Certificates", certId);
1045 getCertificateProperties(asyncResp, objectPath, certs::ldapServiceName,
1046 certId, certURL, "LDAP Certificate");
1047 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1048 << certFile->getCertFilePath();
1049 },
1050 certs::ldapServiceName, certs::ldapObjectPath, certs::certInstallIntf,
1051 "Install", certFile->getCertFilePath());
1052}
Ed Tanous002d39b2022-05-31 08:59:27 -07001053
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001054inline void handleLDAPCertificateGet(
1055 App& app, const crow::Request& req,
1056 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1057{
1058 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1059 {
1060 return;
1061 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001062
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001063 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << id;
1064 const boost::urls::url certURL = crow::utility::urlFromPieces(
1065 "redfish", "v1", "AccountService", "LDAP", "Certificates", id);
1066 std::string objPath =
1067 sdbusplus::message::object_path(certs::ldapObjectPath) / id;
1068 getCertificateProperties(asyncResp, objPath, certs::ldapServiceName, id,
1069 certURL, "LDAP Certificate");
1070}
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001071
Jiaqing Zhao99612242022-09-29 15:31:09 +08001072inline void handleLDAPCertificateDelete(
1073 App& app, const crow::Request& req,
1074 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1075{
1076 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1077 {
1078 return;
1079 }
1080
1081 BMCWEB_LOG_DEBUG << "Delete LDAP Certificate ID=" << id;
1082 std::string objPath =
1083 sdbusplus::message::object_path(certs::ldapObjectPath) / id;
1084
1085 deleteCertificate(asyncResp, certs::ldapServiceName, objPath);
1086}
1087
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001088inline void requestRoutesLDAPCertificate(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001089{
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001090 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1091 .privileges(redfish::privileges::getCertificateCollection)
1092 .methods(boost::beast::http::verb::get)(
1093 std::bind_front(handleLDAPCertificateCollectionGet, std::ref(app)));
1094
1095 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1096 .privileges(redfish::privileges::postCertificateCollection)
1097 .methods(boost::beast::http::verb::post)(std::bind_front(
1098 handleLDAPCertificateCollectionPost, std::ref(app)));
1099
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001100 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001101 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001102 .methods(boost::beast::http::verb::get)(
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001103 std::bind_front(handleLDAPCertificateGet, std::ref(app)));
Jiaqing Zhao99612242022-09-29 15:31:09 +08001104
1105 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
1106 .privileges(redfish::privileges::deleteCertificate)
1107 .methods(boost::beast::http::verb::delete_)(
1108 std::bind_front(handleLDAPCertificateDelete, std::ref(app)));
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001109} // requestRoutesLDAPCertificate
1110
1111inline void handleTrustStoreCertificateCollectionGet(
1112 App& app, const crow::Request& req,
1113 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1114{
1115 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1116 {
1117 return;
1118 }
1119
1120 asyncResp->res.jsonValue["@odata.id"] =
1121 "/redfish/v1/Managers/bmc/Truststore/Certificates/";
1122 asyncResp->res.jsonValue["@odata.type"] =
1123 "#CertificateCollection.CertificateCollection";
1124 asyncResp->res.jsonValue["Name"] = "TrustStore Certificates Collection";
1125 asyncResp->res.jsonValue["Description"] =
1126 "A Collection of TrustStore certificate instances";
1127
1128 getCertificateList(asyncResp, certs::authorityObjectPath,
1129 "/Members"_json_pointer,
1130 "/Members@odata.count"_json_pointer);
1131}
1132
1133inline void handleTrustStoreCertificateCollectionPost(
1134 App& app, const crow::Request& req,
1135 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1136{
1137 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1138 {
1139 return;
1140 }
1141 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1142
1143 if (certFileBody.empty())
1144 {
1145 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1146 messages::unrecognizedRequestBody(asyncResp->res);
1147 return;
1148 }
1149
1150 std::shared_ptr<CertificateFile> certFile =
1151 std::make_shared<CertificateFile>(certFileBody);
1152 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001153 [asyncResp, certFile](const boost::system::error_code& ec,
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001154 const std::string& objectPath) {
1155 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -07001156 {
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001157 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1158 messages::internalError(asyncResp->res);
Ed Tanous002d39b2022-05-31 08:59:27 -07001159 return;
1160 }
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001161
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001162 sdbusplus::message::object_path path(objectPath);
1163 std::string certId = path.filename();
1164 const boost::urls::url certURL =
1165 crow::utility::urlFromPieces("redfish", "v1", "Managers", "bmc",
1166 "Truststore", "Certificates", certId);
1167 getCertificateProperties(asyncResp, objectPath,
1168 certs::authorityServiceName, certId, certURL,
1169 "TrustStore Certificate");
1170 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1171 << certFile->getCertFilePath();
1172 },
1173 certs::authorityServiceName, certs::authorityObjectPath,
1174 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1175}
1176
1177inline void handleTrustStoreCertificateGet(
1178 App& app, const crow::Request& req,
1179 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1180{
1181 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1182 {
1183 return;
1184 }
1185
1186 BMCWEB_LOG_DEBUG << "Truststore Certificate ID=" << id;
1187 const boost::urls::url certURL = crow::utility::urlFromPieces(
1188 "redfish", "v1", "Managers", "bmc", "Truststore", "Certificates", id);
1189 std::string objPath =
1190 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
1191 getCertificateProperties(asyncResp, objPath, certs::authorityServiceName,
1192 id, certURL, "TrustStore Certificate");
1193}
1194
1195inline void handleTrustStoreCertificateDelete(
1196 App& app, const crow::Request& req,
1197 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1198{
1199 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1200 {
1201 return;
1202 }
1203
1204 BMCWEB_LOG_DEBUG << "Delete TrustStore Certificate ID=" << id;
1205 std::string objPath =
1206 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
1207
Jiaqing Zhao7a3a8f72022-09-29 15:15:58 +08001208 deleteCertificate(asyncResp, certs::authorityServiceName, objPath);
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001209}
1210
1211inline void requestRoutesTrustStoreCertificate(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001212{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001213 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001214 .privileges(redfish::privileges::getCertificate)
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001215 .methods(boost::beast::http::verb::get)(std::bind_front(
1216 handleTrustStoreCertificateCollectionGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001217
1218 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001219 .privileges(redfish::privileges::postCertificateCollection)
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001220 .methods(boost::beast::http::verb::post)(std::bind_front(
1221 handleTrustStoreCertificateCollectionPost, std::ref(app)));
Ed Tanous002d39b2022-05-31 08:59:27 -07001222
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001223 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001224 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001225 .methods(boost::beast::http::verb::get)(
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001226 std::bind_front(handleTrustStoreCertificateGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001227
1228 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001229 .privileges(redfish::privileges::deleteCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001230 .methods(boost::beast::http::verb::delete_)(
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001231 std::bind_front(handleTrustStoreCertificateDelete, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001232} // requestRoutesTrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001233} // namespace redfish