blob: 48316174b1d7ad2ea238010d6773172b80d8294c [file] [log] [blame]
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001/*
2// Copyright (c) 2018 IBM Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
17
18#include "node.hpp"
19
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +020020#include <boost/convert.hpp>
21#include <boost/convert/strtol.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050022
Marri Devender Rao5968cae2019-01-21 10:27:12 -060023#include <variant>
24namespace redfish
25{
26namespace certs
27{
Gunnar Mills1214b7e2020-06-04 10:11:30 -050028constexpr char const* httpsObjectPath =
Marri Devender Rao5968cae2019-01-21 10:27:12 -060029 "/xyz/openbmc_project/certs/server/https";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050030constexpr char const* certInstallIntf = "xyz.openbmc_project.Certs.Install";
31constexpr char const* certReplaceIntf = "xyz.openbmc_project.Certs.Replace";
32constexpr char const* objDeleteIntf = "xyz.openbmc_project.Object.Delete";
33constexpr char const* certPropIntf = "xyz.openbmc_project.Certs.Certificate";
34constexpr char const* dbusPropIntf = "org.freedesktop.DBus.Properties";
35constexpr char const* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
36constexpr char const* ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap";
37constexpr char const* httpsServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060038 "xyz.openbmc_project.Certs.Manager.Server.Https";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050039constexpr char const* ldapServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060040 "xyz.openbmc_project.Certs.Manager.Client.Ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050041constexpr char const* authorityServiceName =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050042 "xyz.openbmc_project.Certs.Manager.Authority.Ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050043constexpr char const* authorityObjectPath =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050044 "/xyz/openbmc_project/certs/authority/ldap";
Marri Devender Rao5968cae2019-01-21 10:27:12 -060045} // namespace certs
46
47/**
48 * The Certificate schema defines a Certificate Service which represents the
49 * actions available to manage certificates and links to where certificates
50 * are installed.
51 */
52class CertificateService : public Node
53{
54 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -050055 CertificateService(CrowApp& app) :
Marri Devender Rao5968cae2019-01-21 10:27:12 -060056 Node(app, "/redfish/v1/CertificateService/")
57 {
58 // TODO: Issue#61 No entries are available for Certificate
59 // sevice at https://www.dmtf.org/standards/redfish
60 // "redfish standard registries". Need to modify after DMTF
61 // publish Privilege details for certificate service
62 entityPrivileges = {
63 {boost::beast::http::verb::get, {{"Login"}}},
64 {boost::beast::http::verb::head, {{"Login"}}},
65 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
66 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
67 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
68 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
69 }
70
71 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -050072 void doGet(crow::Response& res, const crow::Request& req,
73 const std::vector<std::string>& params) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -060074 {
75 res.jsonValue = {
76 {"@odata.type", "#CertificateService.v1_0_0.CertificateService"},
77 {"@odata.id", "/redfish/v1/CertificateService"},
Marri Devender Rao5968cae2019-01-21 10:27:12 -060078 {"Id", "CertificateService"},
79 {"Name", "Certificate Service"},
80 {"Description", "Actions available to manage certificates"}};
81 res.jsonValue["CertificateLocations"] = {
82 {"@odata.id",
83 "/redfish/v1/CertificateService/CertificateLocations"}};
84 res.jsonValue["Actions"]["#CertificateService.ReplaceCertificate"] = {
85 {"target", "/redfish/v1/CertificateService/Actions/"
86 "CertificateService.ReplaceCertificate"},
87 {"CertificateType@Redfish.AllowableValues", {"PEM"}}};
Marri Devender Rao30215812019-03-18 08:59:21 -050088 res.jsonValue["Actions"]["#CertificateService.GenerateCSR"] = {
89 {"target", "/redfish/v1/CertificateService/Actions/"
90 "CertificateService.GenerateCSR"}};
Marri Devender Rao5968cae2019-01-21 10:27:12 -060091 res.end();
92 }
93}; // CertificateService
Marri Devender Rao37cce912019-02-20 01:05:22 -060094
Marri Devender Rao5968cae2019-01-21 10:27:12 -060095/**
96 * @brief Find the ID specified in the URL
97 * Finds the numbers specified after the last "/" in the URL and returns.
98 * @param[in] path URL
99 * @return -1 on failure and number on success
100 */
101long getIDFromURL(const std::string_view url)
102{
103 std::size_t found = url.rfind("/");
104 if (found == std::string::npos)
105 {
106 return -1;
107 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200108
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600109 if ((found + 1) < url.length())
110 {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600111 std::string_view str = url.substr(found + 1);
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200112
113 return boost::convert<long>(str, boost::cnv::strtol()).value_or(-1);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600114 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200115
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600116 return -1;
117}
118
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200119std::string
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500120 getCertificateFromReqBody(const std::shared_ptr<AsyncResp>& asyncResp,
121 const crow::Request& req)
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200122{
123 nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false);
124
125 if (reqJson.is_discarded())
126 {
127 // We did not receive JSON request, proceed as it is RAW data
128 return req.body;
129 }
130
131 std::string certificate;
132 std::optional<std::string> certificateType = "PEM";
133
134 if (!json_util::readJson(reqJson, asyncResp->res, "CertificateString",
135 certificate, "CertificateType", certificateType))
136 {
137 BMCWEB_LOG_ERROR << "Required parameters are missing";
138 messages::internalError(asyncResp->res);
139 return std::string();
140 }
141
142 if (*certificateType != "PEM")
143 {
144 messages::propertyValueNotInList(asyncResp->res, *certificateType,
145 "CertificateType");
146 return std::string();
147 }
148
149 return certificate;
150}
151
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600152/**
153 * Class to create a temporary certificate file for uploading to system
154 */
155class CertificateFile
156{
157 public:
158 CertificateFile() = delete;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500159 CertificateFile(const CertificateFile&) = delete;
160 CertificateFile& operator=(const CertificateFile&) = delete;
161 CertificateFile(CertificateFile&&) = delete;
162 CertificateFile& operator=(CertificateFile&&) = delete;
163 CertificateFile(const std::string& certString)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600164 {
165 char dirTemplate[] = "/tmp/Certs.XXXXXX";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500166 char* tempDirectory = mkdtemp(dirTemplate);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600167 if (tempDirectory)
168 {
169 certDirectory = tempDirectory;
170 certificateFile = certDirectory / "cert.pem";
171 std::ofstream out(certificateFile, std::ofstream::out |
172 std::ofstream::binary |
173 std::ofstream::trunc);
174 out << certString;
175 out.close();
176 BMCWEB_LOG_DEBUG << "Creating certificate file" << certificateFile;
177 }
178 }
179 ~CertificateFile()
180 {
181 if (std::filesystem::exists(certDirectory))
182 {
183 BMCWEB_LOG_DEBUG << "Removing certificate file" << certificateFile;
184 try
185 {
186 std::filesystem::remove_all(certDirectory);
187 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500188 catch (const std::filesystem::filesystem_error& e)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600189 {
190 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
191 << certDirectory;
192 }
193 }
194 }
195 std::string getCertFilePath()
196 {
197 return certificateFile;
198 }
199
200 private:
201 std::filesystem::path certificateFile;
202 std::filesystem::path certDirectory;
203};
204
Marri Devender Rao30215812019-03-18 08:59:21 -0500205static std::unique_ptr<sdbusplus::bus::match::match> csrMatcher;
206/**
207 * @brief Read data from CSR D-bus object and set to response
208 *
209 * @param[in] asyncResp Shared pointer to the response message
210 * @param[in] certURI Link to certifiate collection URI
211 * @param[in] service D-Bus service name
212 * @param[in] certObjPath certificate D-Bus object path
213 * @param[in] csrObjPath CSR D-Bus object path
214 * @return None
215 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500216static void getCSR(const std::shared_ptr<AsyncResp>& asyncResp,
217 const std::string& certURI, const std::string& service,
218 const std::string& certObjPath,
219 const std::string& csrObjPath)
Marri Devender Rao30215812019-03-18 08:59:21 -0500220{
221 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
222 << " CSRObjectPath=" << csrObjPath
223 << " service=" << service;
224 crow::connections::systemBus->async_method_call(
225 [asyncResp, certURI](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500226 const std::string& csr) {
Marri Devender Rao30215812019-03-18 08:59:21 -0500227 if (ec)
228 {
229 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
230 messages::internalError(asyncResp->res);
231 return;
232 }
233 if (csr.empty())
234 {
235 BMCWEB_LOG_ERROR << "CSR read is empty";
236 messages::internalError(asyncResp->res);
237 return;
238 }
239 asyncResp->res.jsonValue["CSRString"] = csr;
240 asyncResp->res.jsonValue["CertificateCollection"] = {
241 {"@odata.id", certURI}};
242 },
243 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
244}
245
246/**
247 * Action to Generate CSR
248 */
249class CertificateActionGenerateCSR : public Node
250{
251 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500252 CertificateActionGenerateCSR(CrowApp& app) :
Marri Devender Rao30215812019-03-18 08:59:21 -0500253 Node(app, "/redfish/v1/CertificateService/Actions/"
254 "CertificateService.GenerateCSR/")
255 {
256 entityPrivileges = {
257 {boost::beast::http::verb::get, {{"Login"}}},
258 {boost::beast::http::verb::head, {{"Login"}}},
259 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
260 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
261 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
262 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
263 }
264
265 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500266 void doPost(crow::Response& res, const crow::Request& req,
267 const std::vector<std::string>& params) override
Marri Devender Rao30215812019-03-18 08:59:21 -0500268 {
269 static const int RSA_KEY_BIT_LENGTH = 2048;
270 auto asyncResp = std::make_shared<AsyncResp>(res);
271 // Required parameters
272 std::string city;
273 std::string commonName;
274 std::string country;
275 std::string organization;
276 std::string organizationalUnit;
277 std::string state;
278 nlohmann::json certificateCollection;
279
280 // Optional parameters
281 std::optional<std::vector<std::string>> optAlternativeNames =
282 std::vector<std::string>();
283 std::optional<std::string> optContactPerson = "";
284 std::optional<std::string> optChallengePassword = "";
285 std::optional<std::string> optEmail = "";
286 std::optional<std::string> optGivenName = "";
287 std::optional<std::string> optInitials = "";
288 std::optional<int64_t> optKeyBitLength = RSA_KEY_BIT_LENGTH;
Vernon Maueryaaf32062020-03-09 10:41:31 -0700289 std::optional<std::string> optKeyCurveId = "secp384r1";
Marri Devender Rao30215812019-03-18 08:59:21 -0500290 std::optional<std::string> optKeyPairAlgorithm = "EC";
291 std::optional<std::vector<std::string>> optKeyUsage =
292 std::vector<std::string>();
293 std::optional<std::string> optSurname = "";
294 std::optional<std::string> optUnstructuredName = "";
295 if (!json_util::readJson(
296 req, asyncResp->res, "City", city, "CommonName", commonName,
297 "ContactPerson", optContactPerson, "Country", country,
298 "Organization", organization, "OrganizationalUnit",
299 organizationalUnit, "State", state, "CertificateCollection",
300 certificateCollection, "AlternativeNames", optAlternativeNames,
301 "ChallengePassword", optChallengePassword, "Email", optEmail,
302 "GivenName", optGivenName, "Initials", optInitials,
303 "KeyBitLength", optKeyBitLength, "KeyCurveId", optKeyCurveId,
304 "KeyPairAlgorithm", optKeyPairAlgorithm, "KeyUsage",
305 optKeyUsage, "Surname", optSurname, "UnstructuredName",
306 optUnstructuredName))
307 {
308 return;
309 }
310
311 // bmcweb has no way to store or decode a private key challenge
312 // password, which will likely cause bmcweb to crash on startup if this
313 // is not set on a post so not allowing the user to set value
314 if (*optChallengePassword != "")
315 {
316 messages::actionParameterNotSupported(asyncResp->res, "GenerateCSR",
317 "ChallengePassword");
318 return;
319 }
320
321 std::string certURI;
322 if (!redfish::json_util::readJson(certificateCollection, asyncResp->res,
323 "@odata.id", certURI))
324 {
325 return;
326 }
327
328 std::string objectPath;
329 std::string service;
330 if (boost::starts_with(
331 certURI,
332 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
333 {
334 objectPath = certs::httpsObjectPath;
335 service = certs::httpsServiceName;
336 }
Marri Devender Rao3b7f0142019-05-17 02:53:23 -0500337 else if (boost::starts_with(
338 certURI, "/redfish/v1/AccountService/LDAP/Certificates"))
339 {
340 objectPath = certs::ldapObjectPath;
341 service = certs::ldapServiceName;
342 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500343 else
344 {
345 messages::actionParameterNotSupported(
346 asyncResp->res, "CertificateCollection", "GenerateCSR");
347 return;
348 }
349
350 // supporting only EC and RSA algorithm
351 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA")
352 {
353 messages::actionParameterNotSupported(
354 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
355 return;
356 }
357
358 // supporting only 2048 key bit length for RSA algorithm due to time
359 // consumed in generating private key
360 if (*optKeyPairAlgorithm == "RSA" &&
361 *optKeyBitLength != RSA_KEY_BIT_LENGTH)
362 {
363 messages::propertyValueNotInList(asyncResp->res,
364 std::to_string(*optKeyBitLength),
365 "KeyBitLength");
366 return;
367 }
368
369 // validate KeyUsage supporting only 1 type based on URL
370 if (boost::starts_with(
371 certURI,
372 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
373 {
374 if (optKeyUsage->size() == 0)
375 {
376 optKeyUsage->push_back("ServerAuthentication");
377 }
378 else if (optKeyUsage->size() == 1)
379 {
380 if ((*optKeyUsage)[0] != "ServerAuthentication")
381 {
382 messages::propertyValueNotInList(
383 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
384 return;
385 }
386 }
387 else
388 {
389 messages::actionParameterNotSupported(
390 asyncResp->res, "KeyUsage", "GenerateCSR");
391 return;
392 }
393 }
Marri Devender Rao3b7f0142019-05-17 02:53:23 -0500394 else if (boost::starts_with(
395 certURI, "/redfish/v1/AccountService/LDAP/Certificates"))
396 {
397 if (optKeyUsage->size() == 0)
398 {
399 optKeyUsage->push_back("ClientAuthentication");
400 }
401 else if (optKeyUsage->size() == 1)
402 {
403 if ((*optKeyUsage)[0] != "ClientAuthentication")
404 {
405 messages::propertyValueNotInList(
406 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
407 return;
408 }
409 }
410 else
411 {
412 messages::actionParameterNotSupported(
413 asyncResp->res, "KeyUsage", "GenerateCSR");
414 return;
415 }
416 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500417
418 // Only allow one CSR matcher at a time so setting retry time-out and
419 // timer expiry to 10 seconds for now.
420 static const int TIME_OUT = 10;
421 if (csrMatcher)
422 {
Marri Devender Rao30215812019-03-18 08:59:21 -0500423 messages::serviceTemporarilyUnavailable(asyncResp->res,
424 std::to_string(TIME_OUT));
425 return;
426 }
427
428 // Make this static so it survives outside this method
429 static boost::asio::steady_timer timeout(*req.ioService);
430 timeout.expires_after(std::chrono::seconds(TIME_OUT));
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500431 timeout.async_wait([asyncResp](const boost::system::error_code& ec) {
Marri Devender Rao30215812019-03-18 08:59:21 -0500432 csrMatcher = nullptr;
433 if (ec)
434 {
435 // operation_aborted is expected if timer is canceled before
436 // completion.
437 if (ec != boost::asio::error::operation_aborted)
438 {
439 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
440 }
441 return;
442 }
443 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
444 messages::internalError(asyncResp->res);
445 });
446
447 // create a matcher to wait on CSR object
448 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
449 std::string match("type='signal',"
450 "interface='org.freedesktop.DBus.ObjectManager',"
451 "path='" +
452 objectPath +
453 "',"
454 "member='InterfacesAdded'");
455 csrMatcher = std::make_unique<sdbusplus::bus::match::match>(
456 *crow::connections::systemBus, match,
457 [asyncResp, service, objectPath,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500458 certURI](sdbusplus::message::message& m) {
Ed Tanous271584a2019-07-09 16:24:22 -0700459 timeout.cancel();
Marri Devender Rao30215812019-03-18 08:59:21 -0500460 if (m.is_method_error())
461 {
462 BMCWEB_LOG_ERROR << "Dbus method error!!!";
463 messages::internalError(asyncResp->res);
464 return;
465 }
466 std::vector<std::pair<
467 std::string, std::vector<std::pair<
468 std::string, std::variant<std::string>>>>>
469 interfacesProperties;
470 sdbusplus::message::object_path csrObjectPath;
471 m.read(csrObjectPath, interfacesProperties);
472 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500473 for (auto& interface : interfacesProperties)
Marri Devender Rao30215812019-03-18 08:59:21 -0500474 {
475 if (interface.first == "xyz.openbmc_project.Certs.CSR")
476 {
477 getCSR(asyncResp, certURI, service, objectPath,
478 csrObjectPath.str);
479 break;
480 }
481 }
482 });
483 crow::connections::systemBus->async_method_call(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500484 [asyncResp](const boost::system::error_code& ec,
485 const std::string& path) {
Marri Devender Rao30215812019-03-18 08:59:21 -0500486 if (ec)
487 {
488 BMCWEB_LOG_ERROR << "DBUS response error: " << ec.message();
489 messages::internalError(asyncResp->res);
490 return;
491 }
492 },
493 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
494 "GenerateCSR", *optAlternativeNames, *optChallengePassword, city,
495 commonName, *optContactPerson, country, *optEmail, *optGivenName,
496 *optInitials, *optKeyBitLength, *optKeyCurveId,
497 *optKeyPairAlgorithm, *optKeyUsage, organization,
498 organizationalUnit, state, *optSurname, *optUnstructuredName);
499 }
500}; // CertificateActionGenerateCSR
501
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600502/**
503 * @brief Parse and update Certficate Issue/Subject property
504 *
505 * @param[in] asyncResp Shared pointer to the response message
506 * @param[in] str Issuer/Subject value in key=value pairs
507 * @param[in] type Issuer/Subject
508 * @return None
509 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500510static void updateCertIssuerOrSubject(nlohmann::json& out,
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600511 const std::string_view value)
512{
513 // example: O=openbmc-project.xyz,CN=localhost
514 std::string_view::iterator i = value.begin();
515 while (i != value.end())
516 {
517 std::string_view::iterator tokenBegin = i;
518 while (i != value.end() && *i != '=')
519 {
520 i++;
521 }
522 if (i == value.end())
523 {
524 break;
525 }
Ed Tanous271584a2019-07-09 16:24:22 -0700526 const std::string_view key(tokenBegin,
527 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600528 i++;
529 tokenBegin = i;
530 while (i != value.end() && *i != ',')
531 {
532 i++;
533 }
Ed Tanous271584a2019-07-09 16:24:22 -0700534 const std::string_view val(tokenBegin,
535 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600536 if (key == "L")
537 {
538 out["City"] = val;
539 }
540 else if (key == "CN")
541 {
542 out["CommonName"] = val;
543 }
544 else if (key == "C")
545 {
546 out["Country"] = val;
547 }
548 else if (key == "O")
549 {
550 out["Organization"] = val;
551 }
552 else if (key == "OU")
553 {
554 out["OrganizationalUnit"] = val;
555 }
556 else if (key == "ST")
557 {
558 out["State"] = val;
559 }
560 // skip comma character
561 if (i != value.end())
562 {
563 i++;
564 }
565 }
566}
567
568/**
569 * @brief Retrieve the certificates properties and append to the response
570 * message
571 *
572 * @param[in] asyncResp Shared pointer to the response message
573 * @param[in] objectPath Path of the D-Bus service object
574 * @param[in] certId Id of the certificate
575 * @param[in] certURL URL of the certificate object
576 * @param[in] name name of the certificate
577 * @return None
578 */
579static void getCertificateProperties(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500580 const std::shared_ptr<AsyncResp>& asyncResp, const std::string& objectPath,
581 const std::string& service, long certId, const std::string& certURL,
582 const std::string& name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600583{
584 using PropertyType =
585 std::variant<std::string, uint64_t, std::vector<std::string>>;
586 using PropertiesMap = boost::container::flat_map<std::string, PropertyType>;
587 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
588 << " certId=" << certId << " certURl=" << certURL;
589 crow::connections::systemBus->async_method_call(
Marri Devender Rao37cce912019-02-20 01:05:22 -0600590 [asyncResp, certURL, certId, name](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500591 const PropertiesMap& properties) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600592 if (ec)
593 {
594 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500595 messages::resourceNotFound(asyncResp->res, name,
596 std::to_string(certId));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600597 return;
598 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600599 asyncResp->res.jsonValue = {
600 {"@odata.id", certURL},
601 {"@odata.type", "#Certificate.v1_0_0.Certificate"},
Marri Devender Rao37cce912019-02-20 01:05:22 -0600602 {"Id", std::to_string(certId)},
603 {"Name", name},
604 {"Description", name}};
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500605 for (const auto& property : properties)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600606 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600607 if (property.first == "CertificateString")
608 {
609 asyncResp->res.jsonValue["CertificateString"] = "";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500610 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600611 std::get_if<std::string>(&property.second);
612 if (value)
613 {
614 asyncResp->res.jsonValue["CertificateString"] = *value;
615 }
616 }
617 else if (property.first == "KeyUsage")
618 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500619 nlohmann::json& keyUsage =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600620 asyncResp->res.jsonValue["KeyUsage"];
621 keyUsage = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500622 const std::vector<std::string>* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600623 std::get_if<std::vector<std::string>>(&property.second);
624 if (value)
625 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500626 for (const std::string& usage : *value)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600627 {
628 keyUsage.push_back(usage);
629 }
630 }
631 }
632 else if (property.first == "Issuer")
633 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500634 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600635 std::get_if<std::string>(&property.second);
636 if (value)
637 {
638 updateCertIssuerOrSubject(
639 asyncResp->res.jsonValue["Issuer"], *value);
640 }
641 }
642 else if (property.first == "Subject")
643 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500644 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600645 std::get_if<std::string>(&property.second);
646 if (value)
647 {
648 updateCertIssuerOrSubject(
649 asyncResp->res.jsonValue["Subject"], *value);
650 }
651 }
652 else if (property.first == "ValidNotAfter")
653 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500654 const uint64_t* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600655 std::get_if<uint64_t>(&property.second);
656 if (value)
657 {
658 std::time_t time = static_cast<std::time_t>(*value);
659 asyncResp->res.jsonValue["ValidNotAfter"] =
660 crow::utility::getDateTime(time);
661 }
662 }
663 else if (property.first == "ValidNotBefore")
664 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500665 const uint64_t* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600666 std::get_if<uint64_t>(&property.second);
667 if (value)
668 {
669 std::time_t time = static_cast<std::time_t>(*value);
670 asyncResp->res.jsonValue["ValidNotBefore"] =
671 crow::utility::getDateTime(time);
672 }
673 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600674 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600675 asyncResp->res.addHeader("Location", certURL);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600676 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600677 service, objectPath, certs::dbusPropIntf, "GetAll",
678 certs::certPropIntf);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600679}
680
681using GetObjectType =
682 std::vector<std::pair<std::string, std::vector<std::string>>>;
683
684/**
685 * Action to replace an existing certificate
686 */
687class CertificateActionsReplaceCertificate : public Node
688{
689 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500690 CertificateActionsReplaceCertificate(CrowApp& app) :
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600691 Node(app, "/redfish/v1/CertificateService/Actions/"
692 "CertificateService.ReplaceCertificate/")
693 {
694 entityPrivileges = {
695 {boost::beast::http::verb::get, {{"Login"}}},
696 {boost::beast::http::verb::head, {{"Login"}}},
697 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
698 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
699 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
700 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
701 }
702
703 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500704 void doPost(crow::Response& res, const crow::Request& req,
705 const std::vector<std::string>& params) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600706 {
707 std::string certificate;
708 nlohmann::json certificateUri;
709 std::optional<std::string> certificateType = "PEM";
710 auto asyncResp = std::make_shared<AsyncResp>(res);
711 if (!json_util::readJson(req, asyncResp->res, "CertificateString",
712 certificate, "CertificateUri", certificateUri,
713 "CertificateType", certificateType))
714 {
715 BMCWEB_LOG_ERROR << "Required parameters are missing";
716 messages::internalError(asyncResp->res);
717 return;
718 }
719
720 if (!certificateType)
721 {
722 // should never happen, but it never hurts to be paranoid.
723 return;
724 }
725 if (certificateType != "PEM")
726 {
727 messages::actionParameterNotSupported(
728 asyncResp->res, "CertificateType", "ReplaceCertificate");
729 return;
730 }
731
732 std::string certURI;
733 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
734 "@odata.id", certURI))
735 {
736 messages::actionParameterMissing(
737 asyncResp->res, "ReplaceCertificate", "CertificateUri");
738 return;
739 }
740
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600741 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
742 long id = getIDFromURL(certURI);
743 if (id < 0)
744 {
745 messages::actionParameterValueFormatError(asyncResp->res, certURI,
746 "CertificateUri",
747 "ReplaceCertificate");
748 return;
749 }
750 std::string objectPath;
751 std::string name;
Marri Devender Rao37cce912019-02-20 01:05:22 -0600752 std::string service;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600753 if (boost::starts_with(
754 certURI,
755 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"))
756 {
757 objectPath =
758 std::string(certs::httpsObjectPath) + "/" + std::to_string(id);
759 name = "HTTPS certificate";
Marri Devender Rao37cce912019-02-20 01:05:22 -0600760 service = certs::httpsServiceName;
761 }
762 else if (boost::starts_with(
763 certURI, "/redfish/v1/AccountService/LDAP/Certificates/"))
764 {
765 objectPath =
766 std::string(certs::ldapObjectPath) + "/" + std::to_string(id);
767 name = "LDAP certificate";
768 service = certs::ldapServiceName;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600769 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -0500770 else if (boost::starts_with(
771 certURI,
772 "/redfish/v1/Managers/bmc/Truststore/Certificates/"))
773 {
774 objectPath = std::string(certs::authorityObjectPath) + "/" +
775 std::to_string(id);
776 name = "TrustStore certificate";
777 service = certs::authorityServiceName;
778 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600779 else
780 {
781 messages::actionParameterNotSupported(
782 asyncResp->res, "CertificateUri", "ReplaceCertificate");
783 return;
784 }
785
786 std::shared_ptr<CertificateFile> certFile =
787 std::make_shared<CertificateFile>(certificate);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600788 crow::connections::systemBus->async_method_call(
Marri Devender Rao37cce912019-02-20 01:05:22 -0600789 [asyncResp, certFile, objectPath, service, certURI, id,
790 name](const boost::system::error_code ec) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600791 if (ec)
792 {
793 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500794 messages::resourceNotFound(asyncResp->res, name,
795 std::to_string(id));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600796 return;
797 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600798 getCertificateProperties(asyncResp, objectPath, service, id,
799 certURI, name);
800 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
801 << certFile->getCertFilePath();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600802 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600803 service, objectPath, certs::certReplaceIntf, "Replace",
804 certFile->getCertFilePath());
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600805 }
806}; // CertificateActionsReplaceCertificate
807
808/**
809 * Certificate resource describes a certificate used to prove the identity
810 * of a component, account or service.
811 */
812class HTTPSCertificate : public Node
813{
814 public:
815 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500816 HTTPSCertificate(CrowApp& app) :
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600817 Node(app,
818 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"
819 "<str>/",
820 std::string())
821 {
822 entityPrivileges = {
823 {boost::beast::http::verb::get, {{"Login"}}},
824 {boost::beast::http::verb::head, {{"Login"}}},
825 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
826 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
827 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
828 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
829 }
830
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500831 void doGet(crow::Response& res, const crow::Request& req,
832 const std::vector<std::string>& params) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600833 {
834 auto asyncResp = std::make_shared<AsyncResp>(res);
835 if (params.size() != 1)
836 {
837 messages::internalError(asyncResp->res);
838 return;
839 }
840 long id = getIDFromURL(req.url);
841
842 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID=" << std::to_string(id);
843 std::string certURL =
844 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
845 std::to_string(id);
846 std::string objectPath = certs::httpsObjectPath;
847 objectPath += "/";
848 objectPath += std::to_string(id);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600849 getCertificateProperties(asyncResp, objectPath, certs::httpsServiceName,
850 id, certURL, "HTTPS Certificate");
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600851 }
852
853}; // namespace redfish
854
855/**
856 * Collection of HTTPS certificates
857 */
858class HTTPSCertificateCollection : public Node
859{
860 public:
861 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500862 HTTPSCertificateCollection(CrowApp& app) :
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600863 Node(app,
864 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
865 {
866 entityPrivileges = {
867 {boost::beast::http::verb::get, {{"Login"}}},
868 {boost::beast::http::verb::head, {{"Login"}}},
869 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
870 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
871 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
872 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
873 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500874 void doGet(crow::Response& res, const crow::Request& req,
875 const std::vector<std::string>& params) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600876 {
877 res.jsonValue = {
878 {"@odata.id",
879 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"},
880 {"@odata.type", "#CertificateCollection.CertificateCollection"},
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600881 {"Name", "HTTPS Certificates Collection"},
882 {"Description", "A Collection of HTTPS certificate instances"}};
883 auto asyncResp = std::make_shared<AsyncResp>(res);
884 crow::connections::systemBus->async_method_call(
885 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500886 const ManagedObjectType& certs) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600887 if (ec)
888 {
889 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
890 messages::internalError(asyncResp->res);
891 return;
892 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500893 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
Marri Devender Rao37cce912019-02-20 01:05:22 -0600894 members = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500895 for (const auto& cert : certs)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600896 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600897 long id = getIDFromURL(cert.first.str);
898 if (id >= 0)
899 {
900 members.push_back(
901 {{"@odata.id",
902 "/redfish/v1/Managers/bmc/"
903 "NetworkProtocol/HTTPS/Certificates/" +
904 std::to_string(id)}});
905 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600906 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600907 asyncResp->res.jsonValue["Members@odata.count"] =
908 members.size();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600909 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600910 certs::httpsServiceName, certs::httpsObjectPath,
911 certs::dbusObjManagerIntf, "GetManagedObjects");
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600912 }
913
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500914 void doPost(crow::Response& res, const crow::Request& req,
915 const std::vector<std::string>& params) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600916 {
917 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
918 auto asyncResp = std::make_shared<AsyncResp>(res);
919 asyncResp->res.jsonValue = {{"Name", "HTTPS Certificate"},
920 {"Description", "HTTPS Certificate"}};
921
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200922 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
923
924 if (certFileBody.empty())
925 {
Zbigniew Kurzynskia08752f2019-10-10 18:27:18 +0200926 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
927 messages::unrecognizedRequestBody(asyncResp->res);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200928 return;
929 }
930
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600931 std::shared_ptr<CertificateFile> certFile =
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200932 std::make_shared<CertificateFile>(certFileBody);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600933
934 crow::connections::systemBus->async_method_call(
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200935 [asyncResp, certFile](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500936 const std::string& objectPath) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600937 if (ec)
938 {
939 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
940 messages::internalError(asyncResp->res);
941 return;
942 }
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200943 long certId = getIDFromURL(objectPath);
944 if (certId < 0)
945 {
946 BMCWEB_LOG_ERROR << "Invalid objectPath value"
947 << objectPath;
948 messages::internalError(asyncResp->res);
949 return;
950 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600951 std::string certURL =
952 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/"
953 "Certificates/" +
954 std::to_string(certId);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600955 getCertificateProperties(asyncResp, objectPath,
956 certs::httpsServiceName, certId,
957 certURL, "HTTPS Certificate");
958 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
959 << certFile->getCertFilePath();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600960 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600961 certs::httpsServiceName, certs::httpsObjectPath,
962 certs::certInstallIntf, "Install", certFile->getCertFilePath());
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600963 }
964}; // HTTPSCertificateCollection
965
966/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600967 * The certificate location schema defines a resource that an administrator
968 * can use in order to locate all certificates installed on a given service.
969 */
970class CertificateLocations : public Node
971{
972 public:
973 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500974 CertificateLocations(CrowApp& app) :
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600975 Node(app, "/redfish/v1/CertificateService/CertificateLocations/")
976 {
977 entityPrivileges = {
978 {boost::beast::http::verb::get, {{"Login"}}},
979 {boost::beast::http::verb::head, {{"Login"}}},
980 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
981 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
982 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
983 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
984 }
985
986 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500987 void doGet(crow::Response& res, const crow::Request& req,
988 const std::vector<std::string>& params) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600989 {
990 res.jsonValue = {
991 {"@odata.id",
992 "/redfish/v1/CertificateService/CertificateLocations"},
993 {"@odata.type",
994 "#CertificateLocations.v1_0_0.CertificateLocations"},
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600995 {"Name", "Certificate Locations"},
996 {"Id", "CertificateLocations"},
997 {"Description",
998 "Defines a resource that an administrator can use in order to "
999 "locate all certificates installed on a given service"}};
1000 auto asyncResp = std::make_shared<AsyncResp>(res);
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001001 nlohmann::json& links =
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001002 asyncResp->res.jsonValue["Links"]["Certificates"];
1003 links = nlohmann::json::array();
1004 getCertificateLocations(
1005 asyncResp,
1006 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/",
Marri Devender Rao37cce912019-02-20 01:05:22 -06001007 certs::httpsObjectPath, certs::httpsServiceName);
1008 getCertificateLocations(asyncResp,
1009 "/redfish/v1/AccountService/LDAP/Certificates/",
1010 certs::ldapObjectPath, certs::ldapServiceName);
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001011 getCertificateLocations(
1012 asyncResp, "/redfish/v1/Managers/bmc/Truststore/Certificates/",
1013 certs::authorityObjectPath, certs::authorityServiceName);
Marri Devender Rao37cce912019-02-20 01:05:22 -06001014 }
1015 /**
1016 * @brief Retrieve the certificates installed list and append to the
1017 * response
1018 *
1019 * @param[in] asyncResp Shared pointer to the response message
1020 * @param[in] certURL Path of the certificate object
1021 * @param[in] path Path of the D-Bus service object
1022 * @return None
1023 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001024 void getCertificateLocations(std::shared_ptr<AsyncResp>& asyncResp,
1025 const std::string& certURL,
1026 const std::string& path,
1027 const std::string& service)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001028 {
1029 BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL
1030 << " Path=" << path << " service= " << service;
1031 crow::connections::systemBus->async_method_call(
1032 [asyncResp, certURL](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001033 const ManagedObjectType& certs) {
Marri Devender Rao37cce912019-02-20 01:05:22 -06001034 if (ec)
1035 {
1036 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1037 messages::internalError(asyncResp->res);
1038 return;
1039 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001040 nlohmann::json& links =
Marri Devender Rao37cce912019-02-20 01:05:22 -06001041 asyncResp->res.jsonValue["Links"]["Certificates"];
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001042 for (auto& cert : certs)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001043 {
1044 long id = getIDFromURL(cert.first.str);
1045 if (id >= 0)
1046 {
1047 links.push_back(
1048 {{"@odata.id", certURL + std::to_string(id)}});
1049 }
1050 }
1051 asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] =
1052 links.size();
1053 },
1054 service, path, certs::dbusObjManagerIntf, "GetManagedObjects");
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001055 }
1056}; // CertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -06001057
1058/**
1059 * Collection of LDAP certificates
1060 */
1061class LDAPCertificateCollection : public Node
1062{
1063 public:
1064 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001065 LDAPCertificateCollection(CrowApp& app) :
Marri Devender Rao37cce912019-02-20 01:05:22 -06001066 Node(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1067 {
1068 entityPrivileges = {
1069 {boost::beast::http::verb::get, {{"Login"}}},
1070 {boost::beast::http::verb::head, {{"Login"}}},
1071 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1072 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1073 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1074 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1075 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001076 void doGet(crow::Response& res, const crow::Request& req,
1077 const std::vector<std::string>& params) override
Marri Devender Rao37cce912019-02-20 01:05:22 -06001078 {
1079 res.jsonValue = {
1080 {"@odata.id", "/redfish/v1/AccountService/LDAP/Certificates"},
1081 {"@odata.type", "#CertificateCollection.CertificateCollection"},
Marri Devender Rao37cce912019-02-20 01:05:22 -06001082 {"Name", "LDAP Certificates Collection"},
1083 {"Description", "A Collection of LDAP certificate instances"}};
1084 auto asyncResp = std::make_shared<AsyncResp>(res);
1085 crow::connections::systemBus->async_method_call(
1086 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001087 const ManagedObjectType& certs) {
Marri Devender Rao37cce912019-02-20 01:05:22 -06001088 if (ec)
1089 {
1090 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1091 messages::internalError(asyncResp->res);
1092 return;
1093 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001094 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
Marri Devender Rao37cce912019-02-20 01:05:22 -06001095 members = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001096 for (const auto& cert : certs)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001097 {
1098 long id = getIDFromURL(cert.first.str);
1099 if (id >= 0)
1100 {
1101 members.push_back(
1102 {{"@odata.id", "/redfish/v1/AccountService/"
1103 "LDAP/Certificates/" +
1104 std::to_string(id)}});
1105 }
1106 }
1107 asyncResp->res.jsonValue["Members@odata.count"] =
1108 members.size();
1109 },
1110 certs::ldapServiceName, certs::ldapObjectPath,
1111 certs::dbusObjManagerIntf, "GetManagedObjects");
1112 }
1113
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001114 void doPost(crow::Response& res, const crow::Request& req,
1115 const std::vector<std::string>& params) override
Marri Devender Rao37cce912019-02-20 01:05:22 -06001116 {
Marri Devender Rao37cce912019-02-20 01:05:22 -06001117 auto asyncResp = std::make_shared<AsyncResp>(res);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +02001118 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1119
1120 if (certFileBody.empty())
1121 {
Zbigniew Kurzynskia08752f2019-10-10 18:27:18 +02001122 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1123 messages::unrecognizedRequestBody(asyncResp->res);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +02001124 return;
1125 }
1126
1127 std::shared_ptr<CertificateFile> certFile =
1128 std::make_shared<CertificateFile>(certFileBody);
1129
Marri Devender Rao37cce912019-02-20 01:05:22 -06001130 crow::connections::systemBus->async_method_call(
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001131 [asyncResp, certFile](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001132 const std::string& objectPath) {
Marri Devender Rao37cce912019-02-20 01:05:22 -06001133 if (ec)
1134 {
1135 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1136 messages::internalError(asyncResp->res);
1137 return;
1138 }
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001139 long certId = getIDFromURL(objectPath);
1140 if (certId < 0)
1141 {
1142 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1143 << objectPath;
1144 messages::internalError(asyncResp->res);
1145 return;
1146 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001147 std::string certURL =
1148 "/redfish/v1/AccountService/LDAP/Certificates/" +
1149 std::to_string(certId);
Marri Devender Rao37cce912019-02-20 01:05:22 -06001150 getCertificateProperties(asyncResp, objectPath,
1151 certs::ldapServiceName, certId,
1152 certURL, "LDAP Certificate");
1153 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1154 << certFile->getCertFilePath();
1155 },
1156 certs::ldapServiceName, certs::ldapObjectPath,
1157 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1158 }
1159}; // LDAPCertificateCollection
1160
1161/**
1162 * Certificate resource describes a certificate used to prove the identity
1163 * of a component, account or service.
1164 */
1165class LDAPCertificate : public Node
1166{
1167 public:
1168 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001169 LDAPCertificate(CrowApp& app) :
Marri Devender Rao37cce912019-02-20 01:05:22 -06001170 Node(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/",
1171 std::string())
1172 {
1173 entityPrivileges = {
1174 {boost::beast::http::verb::get, {{"Login"}}},
1175 {boost::beast::http::verb::head, {{"Login"}}},
1176 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1177 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1178 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1179 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1180 }
1181
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001182 void doGet(crow::Response& res, const crow::Request& req,
1183 const std::vector<std::string>& params) override
Marri Devender Rao37cce912019-02-20 01:05:22 -06001184 {
1185 auto asyncResp = std::make_shared<AsyncResp>(res);
1186 long id = getIDFromURL(req.url);
1187 if (id < 0)
1188 {
1189 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1190 messages::internalError(asyncResp->res);
1191 return;
1192 }
1193 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << std::to_string(id);
1194 std::string certURL = "/redfish/v1/AccountService/LDAP/Certificates/" +
1195 std::to_string(id);
1196 std::string objectPath = certs::ldapObjectPath;
1197 objectPath += "/";
1198 objectPath += std::to_string(id);
1199 getCertificateProperties(asyncResp, objectPath, certs::ldapServiceName,
1200 id, certURL, "LDAP Certificate");
1201 }
1202}; // LDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001203/**
1204 * Collection of TrustStoreCertificate certificates
1205 */
1206class TrustStoreCertificateCollection : public Node
1207{
1208 public:
1209 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001210 TrustStoreCertificateCollection(CrowApp& app) :
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001211 Node(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
1212 {
1213 entityPrivileges = {
1214 {boost::beast::http::verb::get, {{"Login"}}},
1215 {boost::beast::http::verb::head, {{"Login"}}},
1216 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1217 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1218 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1219 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1220 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001221 void doGet(crow::Response& res, const crow::Request& req,
1222 const std::vector<std::string>& params) override
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001223 {
1224 res.jsonValue = {
1225 {"@odata.id", "/redfish/v1/Managers/bmc/Truststore/Certificates/"},
1226 {"@odata.type", "#CertificateCollection.CertificateCollection"},
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001227 {"Name", "TrustStore Certificates Collection"},
1228 {"Description",
1229 "A Collection of TrustStore certificate instances"}};
1230 auto asyncResp = std::make_shared<AsyncResp>(res);
1231 crow::connections::systemBus->async_method_call(
1232 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001233 const ManagedObjectType& certs) {
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001234 if (ec)
1235 {
1236 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1237 messages::internalError(asyncResp->res);
1238 return;
1239 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001240 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001241 members = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001242 for (const auto& cert : certs)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001243 {
1244 long id = getIDFromURL(cert.first.str);
1245 if (id >= 0)
1246 {
1247 members.push_back(
1248 {{"@odata.id", "/redfish/v1/Managers/bmc/"
1249 "Truststore/Certificates/" +
1250 std::to_string(id)}});
1251 }
1252 }
1253 asyncResp->res.jsonValue["Members@odata.count"] =
1254 members.size();
1255 },
1256 certs::authorityServiceName, certs::authorityObjectPath,
1257 certs::dbusObjManagerIntf, "GetManagedObjects");
1258 }
1259
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001260 void doPost(crow::Response& res, const crow::Request& req,
1261 const std::vector<std::string>& params) override
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001262 {
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001263 auto asyncResp = std::make_shared<AsyncResp>(res);
Zbigniew Kurzynskia08752f2019-10-10 18:27:18 +02001264 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1265
1266 if (certFileBody.empty())
1267 {
1268 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1269 messages::unrecognizedRequestBody(asyncResp->res);
1270 return;
1271 }
1272
1273 std::shared_ptr<CertificateFile> certFile =
1274 std::make_shared<CertificateFile>(certFileBody);
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001275 crow::connections::systemBus->async_method_call(
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001276 [asyncResp, certFile](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001277 const std::string& objectPath) {
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001278 if (ec)
1279 {
1280 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1281 messages::internalError(asyncResp->res);
1282 return;
1283 }
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001284 long certId = getIDFromURL(objectPath);
1285 if (certId < 0)
1286 {
1287 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1288 << objectPath;
1289 messages::internalError(asyncResp->res);
1290 return;
1291 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001292 std::string certURL = "/redfish/v1/Managers/bmc/"
1293 "Truststore/Certificates/" +
1294 std::to_string(certId);
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001295
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001296 getCertificateProperties(asyncResp, objectPath,
1297 certs::authorityServiceName, certId,
1298 certURL, "TrustStore Certificate");
1299 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1300 << certFile->getCertFilePath();
1301 },
1302 certs::authorityServiceName, certs::authorityObjectPath,
1303 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1304 }
1305}; // TrustStoreCertificateCollection
1306
1307/**
1308 * Certificate resource describes a certificate used to prove the identity
1309 * of a component, account or service.
1310 */
1311class TrustStoreCertificate : public Node
1312{
1313 public:
1314 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001315 TrustStoreCertificate(CrowApp& app) :
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001316 Node(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/",
1317 std::string())
1318 {
1319 entityPrivileges = {
1320 {boost::beast::http::verb::get, {{"Login"}}},
1321 {boost::beast::http::verb::head, {{"Login"}}},
1322 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1323 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1324 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1325 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1326 }
1327
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001328 void doGet(crow::Response& res, const crow::Request& req,
1329 const std::vector<std::string>& params) override
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001330 {
1331 auto asyncResp = std::make_shared<AsyncResp>(res);
1332 long id = getIDFromURL(req.url);
1333 if (id < 0)
1334 {
1335 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1336 messages::internalError(asyncResp->res);
1337 return;
1338 }
1339 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID="
1340 << std::to_string(id);
1341 std::string certURL =
1342 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1343 std::to_string(id);
1344 std::string objectPath = certs::authorityObjectPath;
1345 objectPath += "/";
1346 objectPath += std::to_string(id);
1347 getCertificateProperties(asyncResp, objectPath,
1348 certs::authorityServiceName, id, certURL,
1349 "TrustStore Certificate");
1350 }
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001351
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001352 void doDelete(crow::Response& res, const crow::Request& req,
1353 const std::vector<std::string>& params) override
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001354 {
1355 auto asyncResp = std::make_shared<AsyncResp>(res);
1356
1357 if (params.size() != 1)
1358 {
1359 messages::internalError(asyncResp->res);
1360 return;
1361 }
1362
1363 long id = getIDFromURL(req.url);
1364 if (id < 0)
1365 {
1366 BMCWEB_LOG_ERROR << "Invalid url value: " << req.url;
1367 messages::resourceNotFound(asyncResp->res, "TrustStore Certificate",
1368 std::string(req.url));
1369 return;
1370 }
1371 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doDelete ID="
1372 << std::to_string(id);
1373 std::string certPath = certs::authorityObjectPath;
1374 certPath += "/";
1375 certPath += std::to_string(id);
1376
1377 crow::connections::systemBus->async_method_call(
1378 [asyncResp, id](const boost::system::error_code ec) {
1379 if (ec)
1380 {
1381 messages::resourceNotFound(asyncResp->res,
1382 "TrustStore Certificate",
1383 std::to_string(id));
1384 return;
1385 }
1386 BMCWEB_LOG_INFO << "Certificate deleted";
1387 asyncResp->res.result(boost::beast::http::status::no_content);
1388 },
1389 certs::authorityServiceName, certPath, certs::objDeleteIntf,
1390 "Delete");
1391 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001392}; // TrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001393} // namespace redfish