blob: 6cb5b868ff9ad7c8127af8199a630af5948dbbcf [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>
Marri Devender Rao5968cae2019-01-21 10:27:12 -060022#include <variant>
23namespace redfish
24{
25namespace certs
26{
27constexpr char const *httpsObjectPath =
28 "/xyz/openbmc_project/certs/server/https";
29constexpr char const *certInstallIntf = "xyz.openbmc_project.Certs.Install";
30constexpr char const *certReplaceIntf = "xyz.openbmc_project.Certs.Replace";
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +020031constexpr char const *objDeleteIntf = "xyz.openbmc_project.Object.Delete";
Marri Devender Rao5968cae2019-01-21 10:27:12 -060032constexpr char const *certPropIntf = "xyz.openbmc_project.Certs.Certificate";
33constexpr char const *dbusPropIntf = "org.freedesktop.DBus.Properties";
34constexpr char const *dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
Marri Devender Rao37cce912019-02-20 01:05:22 -060035constexpr char const *ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap";
36constexpr char const *httpsServiceName =
37 "xyz.openbmc_project.Certs.Manager.Server.Https";
38constexpr char const *ldapServiceName =
39 "xyz.openbmc_project.Certs.Manager.Client.Ldap";
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050040constexpr char const *authorityServiceName =
41 "xyz.openbmc_project.Certs.Manager.Authority.Ldap";
42constexpr char const *authorityObjectPath =
43 "/xyz/openbmc_project/certs/authority/ldap";
Marri Devender Rao5968cae2019-01-21 10:27:12 -060044} // namespace certs
45
46/**
47 * The Certificate schema defines a Certificate Service which represents the
48 * actions available to manage certificates and links to where certificates
49 * are installed.
50 */
51class CertificateService : public Node
52{
53 public:
54 CertificateService(CrowApp &app) :
55 Node(app, "/redfish/v1/CertificateService/")
56 {
57 // TODO: Issue#61 No entries are available for Certificate
58 // sevice at https://www.dmtf.org/standards/redfish
59 // "redfish standard registries". Need to modify after DMTF
60 // publish Privilege details for certificate service
61 entityPrivileges = {
62 {boost::beast::http::verb::get, {{"Login"}}},
63 {boost::beast::http::verb::head, {{"Login"}}},
64 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
65 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
66 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
67 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
68 }
69
70 private:
71 void doGet(crow::Response &res, const crow::Request &req,
72 const std::vector<std::string> &params) override
73 {
74 res.jsonValue = {
75 {"@odata.type", "#CertificateService.v1_0_0.CertificateService"},
76 {"@odata.id", "/redfish/v1/CertificateService"},
Marri Devender Rao5968cae2019-01-21 10:27:12 -060077 {"Id", "CertificateService"},
78 {"Name", "Certificate Service"},
79 {"Description", "Actions available to manage certificates"}};
80 res.jsonValue["CertificateLocations"] = {
81 {"@odata.id",
82 "/redfish/v1/CertificateService/CertificateLocations"}};
83 res.jsonValue["Actions"]["#CertificateService.ReplaceCertificate"] = {
84 {"target", "/redfish/v1/CertificateService/Actions/"
85 "CertificateService.ReplaceCertificate"},
86 {"CertificateType@Redfish.AllowableValues", {"PEM"}}};
Marri Devender Rao30215812019-03-18 08:59:21 -050087 res.jsonValue["Actions"]["#CertificateService.GenerateCSR"] = {
88 {"target", "/redfish/v1/CertificateService/Actions/"
89 "CertificateService.GenerateCSR"}};
Marri Devender Rao5968cae2019-01-21 10:27:12 -060090 res.end();
91 }
92}; // CertificateService
Marri Devender Rao37cce912019-02-20 01:05:22 -060093
Marri Devender Rao5968cae2019-01-21 10:27:12 -060094/**
95 * @brief Find the ID specified in the URL
96 * Finds the numbers specified after the last "/" in the URL and returns.
97 * @param[in] path URL
98 * @return -1 on failure and number on success
99 */
100long getIDFromURL(const std::string_view url)
101{
102 std::size_t found = url.rfind("/");
103 if (found == std::string::npos)
104 {
105 return -1;
106 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200107
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600108 if ((found + 1) < url.length())
109 {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600110 std::string_view str = url.substr(found + 1);
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200111
112 return boost::convert<long>(str, boost::cnv::strtol()).value_or(-1);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600113 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200114
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600115 return -1;
116}
117
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200118std::string
119 getCertificateFromReqBody(const std::shared_ptr<AsyncResp> &asyncResp,
120 const crow::Request &req)
121{
122 nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false);
123
124 if (reqJson.is_discarded())
125 {
126 // We did not receive JSON request, proceed as it is RAW data
127 return req.body;
128 }
129
130 std::string certificate;
131 std::optional<std::string> certificateType = "PEM";
132
133 if (!json_util::readJson(reqJson, asyncResp->res, "CertificateString",
134 certificate, "CertificateType", certificateType))
135 {
136 BMCWEB_LOG_ERROR << "Required parameters are missing";
137 messages::internalError(asyncResp->res);
138 return std::string();
139 }
140
141 if (*certificateType != "PEM")
142 {
143 messages::propertyValueNotInList(asyncResp->res, *certificateType,
144 "CertificateType");
145 return std::string();
146 }
147
148 return certificate;
149}
150
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600151/**
152 * Class to create a temporary certificate file for uploading to system
153 */
154class CertificateFile
155{
156 public:
157 CertificateFile() = delete;
158 CertificateFile(const CertificateFile &) = delete;
159 CertificateFile &operator=(const CertificateFile &) = delete;
160 CertificateFile(CertificateFile &&) = delete;
161 CertificateFile &operator=(CertificateFile &&) = delete;
162 CertificateFile(const std::string &certString)
163 {
164 char dirTemplate[] = "/tmp/Certs.XXXXXX";
165 char *tempDirectory = mkdtemp(dirTemplate);
166 if (tempDirectory)
167 {
168 certDirectory = tempDirectory;
169 certificateFile = certDirectory / "cert.pem";
170 std::ofstream out(certificateFile, std::ofstream::out |
171 std::ofstream::binary |
172 std::ofstream::trunc);
173 out << certString;
174 out.close();
175 BMCWEB_LOG_DEBUG << "Creating certificate file" << certificateFile;
176 }
177 }
178 ~CertificateFile()
179 {
180 if (std::filesystem::exists(certDirectory))
181 {
182 BMCWEB_LOG_DEBUG << "Removing certificate file" << certificateFile;
183 try
184 {
185 std::filesystem::remove_all(certDirectory);
186 }
187 catch (const std::filesystem::filesystem_error &e)
188 {
189 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
190 << certDirectory;
191 }
192 }
193 }
194 std::string getCertFilePath()
195 {
196 return certificateFile;
197 }
198
199 private:
200 std::filesystem::path certificateFile;
201 std::filesystem::path certDirectory;
202};
203
Marri Devender Rao30215812019-03-18 08:59:21 -0500204static std::unique_ptr<sdbusplus::bus::match::match> csrMatcher;
205/**
206 * @brief Read data from CSR D-bus object and set to response
207 *
208 * @param[in] asyncResp Shared pointer to the response message
209 * @param[in] certURI Link to certifiate collection URI
210 * @param[in] service D-Bus service name
211 * @param[in] certObjPath certificate D-Bus object path
212 * @param[in] csrObjPath CSR D-Bus object path
213 * @return None
214 */
215static void getCSR(const std::shared_ptr<AsyncResp> &asyncResp,
216 const std::string &certURI, const std::string &service,
217 const std::string &certObjPath,
218 const std::string &csrObjPath)
219{
220 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
221 << " CSRObjectPath=" << csrObjPath
222 << " service=" << service;
223 crow::connections::systemBus->async_method_call(
224 [asyncResp, certURI](const boost::system::error_code ec,
225 const std::string &csr) {
226 if (ec)
227 {
228 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
229 messages::internalError(asyncResp->res);
230 return;
231 }
232 if (csr.empty())
233 {
234 BMCWEB_LOG_ERROR << "CSR read is empty";
235 messages::internalError(asyncResp->res);
236 return;
237 }
238 asyncResp->res.jsonValue["CSRString"] = csr;
239 asyncResp->res.jsonValue["CertificateCollection"] = {
240 {"@odata.id", certURI}};
241 },
242 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
243}
244
245/**
246 * Action to Generate CSR
247 */
248class CertificateActionGenerateCSR : public Node
249{
250 public:
251 CertificateActionGenerateCSR(CrowApp &app) :
252 Node(app, "/redfish/v1/CertificateService/Actions/"
253 "CertificateService.GenerateCSR/")
254 {
255 entityPrivileges = {
256 {boost::beast::http::verb::get, {{"Login"}}},
257 {boost::beast::http::verb::head, {{"Login"}}},
258 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
259 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
260 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
261 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
262 }
263
264 private:
265 void doPost(crow::Response &res, const crow::Request &req,
266 const std::vector<std::string> &params) override
267 {
268 static const int RSA_KEY_BIT_LENGTH = 2048;
269 auto asyncResp = std::make_shared<AsyncResp>(res);
270 // Required parameters
271 std::string city;
272 std::string commonName;
273 std::string country;
274 std::string organization;
275 std::string organizationalUnit;
276 std::string state;
277 nlohmann::json certificateCollection;
278
279 // Optional parameters
280 std::optional<std::vector<std::string>> optAlternativeNames =
281 std::vector<std::string>();
282 std::optional<std::string> optContactPerson = "";
283 std::optional<std::string> optChallengePassword = "";
284 std::optional<std::string> optEmail = "";
285 std::optional<std::string> optGivenName = "";
286 std::optional<std::string> optInitials = "";
287 std::optional<int64_t> optKeyBitLength = RSA_KEY_BIT_LENGTH;
288 std::optional<std::string> optKeyCurveId = "prime256v1";
289 std::optional<std::string> optKeyPairAlgorithm = "EC";
290 std::optional<std::vector<std::string>> optKeyUsage =
291 std::vector<std::string>();
292 std::optional<std::string> optSurname = "";
293 std::optional<std::string> optUnstructuredName = "";
294 if (!json_util::readJson(
295 req, asyncResp->res, "City", city, "CommonName", commonName,
296 "ContactPerson", optContactPerson, "Country", country,
297 "Organization", organization, "OrganizationalUnit",
298 organizationalUnit, "State", state, "CertificateCollection",
299 certificateCollection, "AlternativeNames", optAlternativeNames,
300 "ChallengePassword", optChallengePassword, "Email", optEmail,
301 "GivenName", optGivenName, "Initials", optInitials,
302 "KeyBitLength", optKeyBitLength, "KeyCurveId", optKeyCurveId,
303 "KeyPairAlgorithm", optKeyPairAlgorithm, "KeyUsage",
304 optKeyUsage, "Surname", optSurname, "UnstructuredName",
305 optUnstructuredName))
306 {
307 return;
308 }
309
310 // bmcweb has no way to store or decode a private key challenge
311 // password, which will likely cause bmcweb to crash on startup if this
312 // is not set on a post so not allowing the user to set value
313 if (*optChallengePassword != "")
314 {
315 messages::actionParameterNotSupported(asyncResp->res, "GenerateCSR",
316 "ChallengePassword");
317 return;
318 }
319
320 std::string certURI;
321 if (!redfish::json_util::readJson(certificateCollection, asyncResp->res,
322 "@odata.id", certURI))
323 {
324 return;
325 }
326
327 std::string objectPath;
328 std::string service;
329 if (boost::starts_with(
330 certURI,
331 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
332 {
333 objectPath = certs::httpsObjectPath;
334 service = certs::httpsServiceName;
335 }
Marri Devender Rao3b7f0142019-05-17 02:53:23 -0500336 else if (boost::starts_with(
337 certURI, "/redfish/v1/AccountService/LDAP/Certificates"))
338 {
339 objectPath = certs::ldapObjectPath;
340 service = certs::ldapServiceName;
341 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500342 else
343 {
344 messages::actionParameterNotSupported(
345 asyncResp->res, "CertificateCollection", "GenerateCSR");
346 return;
347 }
348
349 // supporting only EC and RSA algorithm
350 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA")
351 {
352 messages::actionParameterNotSupported(
353 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
354 return;
355 }
356
357 // supporting only 2048 key bit length for RSA algorithm due to time
358 // consumed in generating private key
359 if (*optKeyPairAlgorithm == "RSA" &&
360 *optKeyBitLength != RSA_KEY_BIT_LENGTH)
361 {
362 messages::propertyValueNotInList(asyncResp->res,
363 std::to_string(*optKeyBitLength),
364 "KeyBitLength");
365 return;
366 }
367
368 // validate KeyUsage supporting only 1 type based on URL
369 if (boost::starts_with(
370 certURI,
371 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
372 {
373 if (optKeyUsage->size() == 0)
374 {
375 optKeyUsage->push_back("ServerAuthentication");
376 }
377 else if (optKeyUsage->size() == 1)
378 {
379 if ((*optKeyUsage)[0] != "ServerAuthentication")
380 {
381 messages::propertyValueNotInList(
382 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
383 return;
384 }
385 }
386 else
387 {
388 messages::actionParameterNotSupported(
389 asyncResp->res, "KeyUsage", "GenerateCSR");
390 return;
391 }
392 }
Marri Devender Rao3b7f0142019-05-17 02:53:23 -0500393 else if (boost::starts_with(
394 certURI, "/redfish/v1/AccountService/LDAP/Certificates"))
395 {
396 if (optKeyUsage->size() == 0)
397 {
398 optKeyUsage->push_back("ClientAuthentication");
399 }
400 else if (optKeyUsage->size() == 1)
401 {
402 if ((*optKeyUsage)[0] != "ClientAuthentication")
403 {
404 messages::propertyValueNotInList(
405 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
406 return;
407 }
408 }
409 else
410 {
411 messages::actionParameterNotSupported(
412 asyncResp->res, "KeyUsage", "GenerateCSR");
413 return;
414 }
415 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500416
417 // Only allow one CSR matcher at a time so setting retry time-out and
418 // timer expiry to 10 seconds for now.
419 static const int TIME_OUT = 10;
420 if (csrMatcher)
421 {
Marri Devender Rao30215812019-03-18 08:59:21 -0500422 messages::serviceTemporarilyUnavailable(asyncResp->res,
423 std::to_string(TIME_OUT));
424 return;
425 }
426
427 // Make this static so it survives outside this method
428 static boost::asio::steady_timer timeout(*req.ioService);
429 timeout.expires_after(std::chrono::seconds(TIME_OUT));
430 timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
431 csrMatcher = nullptr;
432 if (ec)
433 {
434 // operation_aborted is expected if timer is canceled before
435 // completion.
436 if (ec != boost::asio::error::operation_aborted)
437 {
438 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
439 }
440 return;
441 }
442 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
443 messages::internalError(asyncResp->res);
444 });
445
446 // create a matcher to wait on CSR object
447 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
448 std::string match("type='signal',"
449 "interface='org.freedesktop.DBus.ObjectManager',"
450 "path='" +
451 objectPath +
452 "',"
453 "member='InterfacesAdded'");
454 csrMatcher = std::make_unique<sdbusplus::bus::match::match>(
455 *crow::connections::systemBus, match,
456 [asyncResp, service, objectPath,
457 certURI](sdbusplus::message::message &m) {
Ed Tanous271584a2019-07-09 16:24:22 -0700458 timeout.cancel();
Marri Devender Rao30215812019-03-18 08:59:21 -0500459 if (m.is_method_error())
460 {
461 BMCWEB_LOG_ERROR << "Dbus method error!!!";
462 messages::internalError(asyncResp->res);
463 return;
464 }
465 std::vector<std::pair<
466 std::string, std::vector<std::pair<
467 std::string, std::variant<std::string>>>>>
468 interfacesProperties;
469 sdbusplus::message::object_path csrObjectPath;
470 m.read(csrObjectPath, interfacesProperties);
471 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
472 for (auto &interface : interfacesProperties)
473 {
474 if (interface.first == "xyz.openbmc_project.Certs.CSR")
475 {
476 getCSR(asyncResp, certURI, service, objectPath,
477 csrObjectPath.str);
478 break;
479 }
480 }
481 });
482 crow::connections::systemBus->async_method_call(
Ed Tanous271584a2019-07-09 16:24:22 -0700483 [asyncResp](const boost::system::error_code &ec,
Marri Devender Rao30215812019-03-18 08:59:21 -0500484 const std::string &path) {
485 if (ec)
486 {
487 BMCWEB_LOG_ERROR << "DBUS response error: " << ec.message();
488 messages::internalError(asyncResp->res);
489 return;
490 }
491 },
492 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
493 "GenerateCSR", *optAlternativeNames, *optChallengePassword, city,
494 commonName, *optContactPerson, country, *optEmail, *optGivenName,
495 *optInitials, *optKeyBitLength, *optKeyCurveId,
496 *optKeyPairAlgorithm, *optKeyUsage, organization,
497 organizationalUnit, state, *optSurname, *optUnstructuredName);
498 }
499}; // CertificateActionGenerateCSR
500
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600501/**
502 * @brief Parse and update Certficate Issue/Subject property
503 *
504 * @param[in] asyncResp Shared pointer to the response message
505 * @param[in] str Issuer/Subject value in key=value pairs
506 * @param[in] type Issuer/Subject
507 * @return None
508 */
509static void updateCertIssuerOrSubject(nlohmann::json &out,
510 const std::string_view value)
511{
512 // example: O=openbmc-project.xyz,CN=localhost
513 std::string_view::iterator i = value.begin();
514 while (i != value.end())
515 {
516 std::string_view::iterator tokenBegin = i;
517 while (i != value.end() && *i != '=')
518 {
519 i++;
520 }
521 if (i == value.end())
522 {
523 break;
524 }
Ed Tanous271584a2019-07-09 16:24:22 -0700525 const std::string_view key(tokenBegin,
526 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600527 i++;
528 tokenBegin = i;
529 while (i != value.end() && *i != ',')
530 {
531 i++;
532 }
Ed Tanous271584a2019-07-09 16:24:22 -0700533 const std::string_view val(tokenBegin,
534 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600535 if (key == "L")
536 {
537 out["City"] = val;
538 }
539 else if (key == "CN")
540 {
541 out["CommonName"] = val;
542 }
543 else if (key == "C")
544 {
545 out["Country"] = val;
546 }
547 else if (key == "O")
548 {
549 out["Organization"] = val;
550 }
551 else if (key == "OU")
552 {
553 out["OrganizationalUnit"] = val;
554 }
555 else if (key == "ST")
556 {
557 out["State"] = val;
558 }
559 // skip comma character
560 if (i != value.end())
561 {
562 i++;
563 }
564 }
565}
566
567/**
568 * @brief Retrieve the certificates properties and append to the response
569 * message
570 *
571 * @param[in] asyncResp Shared pointer to the response message
572 * @param[in] objectPath Path of the D-Bus service object
573 * @param[in] certId Id of the certificate
574 * @param[in] certURL URL of the certificate object
575 * @param[in] name name of the certificate
576 * @return None
577 */
578static void getCertificateProperties(
579 const std::shared_ptr<AsyncResp> &asyncResp, const std::string &objectPath,
Marri Devender Rao37cce912019-02-20 01:05:22 -0600580 const std::string &service, long certId, const std::string &certURL,
581 const std::string &name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600582{
583 using PropertyType =
584 std::variant<std::string, uint64_t, std::vector<std::string>>;
585 using PropertiesMap = boost::container::flat_map<std::string, PropertyType>;
586 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
587 << " certId=" << certId << " certURl=" << certURL;
588 crow::connections::systemBus->async_method_call(
Marri Devender Rao37cce912019-02-20 01:05:22 -0600589 [asyncResp, certURL, certId, name](const boost::system::error_code ec,
590 const PropertiesMap &properties) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600591 if (ec)
592 {
593 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500594 messages::resourceNotFound(asyncResp->res, name,
595 std::to_string(certId));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600596 return;
597 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600598 asyncResp->res.jsonValue = {
599 {"@odata.id", certURL},
600 {"@odata.type", "#Certificate.v1_0_0.Certificate"},
Marri Devender Rao37cce912019-02-20 01:05:22 -0600601 {"Id", std::to_string(certId)},
602 {"Name", name},
603 {"Description", name}};
604 for (const auto &property : properties)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600605 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600606 if (property.first == "CertificateString")
607 {
608 asyncResp->res.jsonValue["CertificateString"] = "";
609 const std::string *value =
610 std::get_if<std::string>(&property.second);
611 if (value)
612 {
613 asyncResp->res.jsonValue["CertificateString"] = *value;
614 }
615 }
616 else if (property.first == "KeyUsage")
617 {
618 nlohmann::json &keyUsage =
619 asyncResp->res.jsonValue["KeyUsage"];
620 keyUsage = nlohmann::json::array();
621 const std::vector<std::string> *value =
622 std::get_if<std::vector<std::string>>(&property.second);
623 if (value)
624 {
625 for (const std::string &usage : *value)
626 {
627 keyUsage.push_back(usage);
628 }
629 }
630 }
631 else if (property.first == "Issuer")
632 {
633 const std::string *value =
634 std::get_if<std::string>(&property.second);
635 if (value)
636 {
637 updateCertIssuerOrSubject(
638 asyncResp->res.jsonValue["Issuer"], *value);
639 }
640 }
641 else if (property.first == "Subject")
642 {
643 const std::string *value =
644 std::get_if<std::string>(&property.second);
645 if (value)
646 {
647 updateCertIssuerOrSubject(
648 asyncResp->res.jsonValue["Subject"], *value);
649 }
650 }
651 else if (property.first == "ValidNotAfter")
652 {
653 const uint64_t *value =
654 std::get_if<uint64_t>(&property.second);
655 if (value)
656 {
657 std::time_t time = static_cast<std::time_t>(*value);
658 asyncResp->res.jsonValue["ValidNotAfter"] =
659 crow::utility::getDateTime(time);
660 }
661 }
662 else if (property.first == "ValidNotBefore")
663 {
664 const uint64_t *value =
665 std::get_if<uint64_t>(&property.second);
666 if (value)
667 {
668 std::time_t time = static_cast<std::time_t>(*value);
669 asyncResp->res.jsonValue["ValidNotBefore"] =
670 crow::utility::getDateTime(time);
671 }
672 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600673 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600674 asyncResp->res.addHeader("Location", certURL);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600675 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600676 service, objectPath, certs::dbusPropIntf, "GetAll",
677 certs::certPropIntf);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600678}
679
680using GetObjectType =
681 std::vector<std::pair<std::string, std::vector<std::string>>>;
682
683/**
684 * Action to replace an existing certificate
685 */
686class CertificateActionsReplaceCertificate : public Node
687{
688 public:
689 CertificateActionsReplaceCertificate(CrowApp &app) :
690 Node(app, "/redfish/v1/CertificateService/Actions/"
691 "CertificateService.ReplaceCertificate/")
692 {
693 entityPrivileges = {
694 {boost::beast::http::verb::get, {{"Login"}}},
695 {boost::beast::http::verb::head, {{"Login"}}},
696 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
697 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
698 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
699 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
700 }
701
702 private:
703 void doPost(crow::Response &res, const crow::Request &req,
704 const std::vector<std::string> &params) override
705 {
706 std::string certificate;
707 nlohmann::json certificateUri;
708 std::optional<std::string> certificateType = "PEM";
709 auto asyncResp = std::make_shared<AsyncResp>(res);
710 if (!json_util::readJson(req, asyncResp->res, "CertificateString",
711 certificate, "CertificateUri", certificateUri,
712 "CertificateType", certificateType))
713 {
714 BMCWEB_LOG_ERROR << "Required parameters are missing";
715 messages::internalError(asyncResp->res);
716 return;
717 }
718
719 if (!certificateType)
720 {
721 // should never happen, but it never hurts to be paranoid.
722 return;
723 }
724 if (certificateType != "PEM")
725 {
726 messages::actionParameterNotSupported(
727 asyncResp->res, "CertificateType", "ReplaceCertificate");
728 return;
729 }
730
731 std::string certURI;
732 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
733 "@odata.id", certURI))
734 {
735 messages::actionParameterMissing(
736 asyncResp->res, "ReplaceCertificate", "CertificateUri");
737 return;
738 }
739
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600740 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
741 long id = getIDFromURL(certURI);
742 if (id < 0)
743 {
744 messages::actionParameterValueFormatError(asyncResp->res, certURI,
745 "CertificateUri",
746 "ReplaceCertificate");
747 return;
748 }
749 std::string objectPath;
750 std::string name;
Marri Devender Rao37cce912019-02-20 01:05:22 -0600751 std::string service;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600752 if (boost::starts_with(
753 certURI,
754 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"))
755 {
756 objectPath =
757 std::string(certs::httpsObjectPath) + "/" + std::to_string(id);
758 name = "HTTPS certificate";
Marri Devender Rao37cce912019-02-20 01:05:22 -0600759 service = certs::httpsServiceName;
760 }
761 else if (boost::starts_with(
762 certURI, "/redfish/v1/AccountService/LDAP/Certificates/"))
763 {
764 objectPath =
765 std::string(certs::ldapObjectPath) + "/" + std::to_string(id);
766 name = "LDAP certificate";
767 service = certs::ldapServiceName;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600768 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -0500769 else if (boost::starts_with(
770 certURI,
771 "/redfish/v1/Managers/bmc/Truststore/Certificates/"))
772 {
773 objectPath = std::string(certs::authorityObjectPath) + "/" +
774 std::to_string(id);
775 name = "TrustStore certificate";
776 service = certs::authorityServiceName;
777 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600778 else
779 {
780 messages::actionParameterNotSupported(
781 asyncResp->res, "CertificateUri", "ReplaceCertificate");
782 return;
783 }
784
785 std::shared_ptr<CertificateFile> certFile =
786 std::make_shared<CertificateFile>(certificate);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600787 crow::connections::systemBus->async_method_call(
Marri Devender Rao37cce912019-02-20 01:05:22 -0600788 [asyncResp, certFile, objectPath, service, certURI, id,
789 name](const boost::system::error_code ec) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600790 if (ec)
791 {
792 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500793 messages::resourceNotFound(asyncResp->res, name,
794 std::to_string(id));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600795 return;
796 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600797 getCertificateProperties(asyncResp, objectPath, service, id,
798 certURI, name);
799 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
800 << certFile->getCertFilePath();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600801 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600802 service, objectPath, certs::certReplaceIntf, "Replace",
803 certFile->getCertFilePath());
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600804 }
805}; // CertificateActionsReplaceCertificate
806
807/**
808 * Certificate resource describes a certificate used to prove the identity
809 * of a component, account or service.
810 */
811class HTTPSCertificate : public Node
812{
813 public:
814 template <typename CrowApp>
815 HTTPSCertificate(CrowApp &app) :
816 Node(app,
817 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"
818 "<str>/",
819 std::string())
820 {
821 entityPrivileges = {
822 {boost::beast::http::verb::get, {{"Login"}}},
823 {boost::beast::http::verb::head, {{"Login"}}},
824 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
825 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
826 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
827 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
828 }
829
830 void doGet(crow::Response &res, const crow::Request &req,
831 const std::vector<std::string> &params) override
832 {
833 auto asyncResp = std::make_shared<AsyncResp>(res);
834 if (params.size() != 1)
835 {
836 messages::internalError(asyncResp->res);
837 return;
838 }
839 long id = getIDFromURL(req.url);
840
841 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID=" << std::to_string(id);
842 std::string certURL =
843 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
844 std::to_string(id);
845 std::string objectPath = certs::httpsObjectPath;
846 objectPath += "/";
847 objectPath += std::to_string(id);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600848 getCertificateProperties(asyncResp, objectPath, certs::httpsServiceName,
849 id, certURL, "HTTPS Certificate");
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600850 }
851
852}; // namespace redfish
853
854/**
855 * Collection of HTTPS certificates
856 */
857class HTTPSCertificateCollection : public Node
858{
859 public:
860 template <typename CrowApp>
861 HTTPSCertificateCollection(CrowApp &app) :
862 Node(app,
863 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
864 {
865 entityPrivileges = {
866 {boost::beast::http::verb::get, {{"Login"}}},
867 {boost::beast::http::verb::head, {{"Login"}}},
868 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
869 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
870 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
871 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
872 }
873 void doGet(crow::Response &res, const crow::Request &req,
874 const std::vector<std::string> &params) override
875 {
876 res.jsonValue = {
877 {"@odata.id",
878 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"},
879 {"@odata.type", "#CertificateCollection.CertificateCollection"},
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600880 {"Name", "HTTPS Certificates Collection"},
881 {"Description", "A Collection of HTTPS certificate instances"}};
882 auto asyncResp = std::make_shared<AsyncResp>(res);
883 crow::connections::systemBus->async_method_call(
884 [asyncResp](const boost::system::error_code ec,
Marri Devender Rao37cce912019-02-20 01:05:22 -0600885 const ManagedObjectType &certs) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600886 if (ec)
887 {
888 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
889 messages::internalError(asyncResp->res);
890 return;
891 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600892 nlohmann::json &members = asyncResp->res.jsonValue["Members"];
893 members = nlohmann::json::array();
894 for (const auto &cert : certs)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600895 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600896 long id = getIDFromURL(cert.first.str);
897 if (id >= 0)
898 {
899 members.push_back(
900 {{"@odata.id",
901 "/redfish/v1/Managers/bmc/"
902 "NetworkProtocol/HTTPS/Certificates/" +
903 std::to_string(id)}});
904 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600905 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600906 asyncResp->res.jsonValue["Members@odata.count"] =
907 members.size();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600908 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600909 certs::httpsServiceName, certs::httpsObjectPath,
910 certs::dbusObjManagerIntf, "GetManagedObjects");
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600911 }
912
913 void doPost(crow::Response &res, const crow::Request &req,
914 const std::vector<std::string> &params) override
915 {
916 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
917 auto asyncResp = std::make_shared<AsyncResp>(res);
918 asyncResp->res.jsonValue = {{"Name", "HTTPS Certificate"},
919 {"Description", "HTTPS Certificate"}};
920
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200921 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
922
923 if (certFileBody.empty())
924 {
Zbigniew Kurzynskia08752f2019-10-10 18:27:18 +0200925 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
926 messages::unrecognizedRequestBody(asyncResp->res);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200927 return;
928 }
929
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600930 std::shared_ptr<CertificateFile> certFile =
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200931 std::make_shared<CertificateFile>(certFileBody);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600932
933 crow::connections::systemBus->async_method_call(
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200934 [asyncResp, certFile](const boost::system::error_code ec,
935 const std::string &objectPath) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600936 if (ec)
937 {
938 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
939 messages::internalError(asyncResp->res);
940 return;
941 }
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200942 long certId = getIDFromURL(objectPath);
943 if (certId < 0)
944 {
945 BMCWEB_LOG_ERROR << "Invalid objectPath value"
946 << objectPath;
947 messages::internalError(asyncResp->res);
948 return;
949 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600950 std::string certURL =
951 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/"
952 "Certificates/" +
953 std::to_string(certId);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600954 getCertificateProperties(asyncResp, objectPath,
955 certs::httpsServiceName, certId,
956 certURL, "HTTPS Certificate");
957 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
958 << certFile->getCertFilePath();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600959 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600960 certs::httpsServiceName, certs::httpsObjectPath,
961 certs::certInstallIntf, "Install", certFile->getCertFilePath());
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600962 }
963}; // HTTPSCertificateCollection
964
965/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600966 * The certificate location schema defines a resource that an administrator
967 * can use in order to locate all certificates installed on a given service.
968 */
969class CertificateLocations : public Node
970{
971 public:
972 template <typename CrowApp>
973 CertificateLocations(CrowApp &app) :
974 Node(app, "/redfish/v1/CertificateService/CertificateLocations/")
975 {
976 entityPrivileges = {
977 {boost::beast::http::verb::get, {{"Login"}}},
978 {boost::beast::http::verb::head, {{"Login"}}},
979 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
980 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
981 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
982 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
983 }
984
985 private:
986 void doGet(crow::Response &res, const crow::Request &req,
987 const std::vector<std::string> &params) override
988 {
989 res.jsonValue = {
990 {"@odata.id",
991 "/redfish/v1/CertificateService/CertificateLocations"},
992 {"@odata.type",
993 "#CertificateLocations.v1_0_0.CertificateLocations"},
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600994 {"Name", "Certificate Locations"},
995 {"Id", "CertificateLocations"},
996 {"Description",
997 "Defines a resource that an administrator can use in order to "
998 "locate all certificates installed on a given service"}};
999 auto asyncResp = std::make_shared<AsyncResp>(res);
1000 nlohmann::json &links =
1001 asyncResp->res.jsonValue["Links"]["Certificates"];
1002 links = nlohmann::json::array();
1003 getCertificateLocations(
1004 asyncResp,
1005 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/",
Marri Devender Rao37cce912019-02-20 01:05:22 -06001006 certs::httpsObjectPath, certs::httpsServiceName);
1007 getCertificateLocations(asyncResp,
1008 "/redfish/v1/AccountService/LDAP/Certificates/",
1009 certs::ldapObjectPath, certs::ldapServiceName);
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001010 getCertificateLocations(
1011 asyncResp, "/redfish/v1/Managers/bmc/Truststore/Certificates/",
1012 certs::authorityObjectPath, certs::authorityServiceName);
Marri Devender Rao37cce912019-02-20 01:05:22 -06001013 }
1014 /**
1015 * @brief Retrieve the certificates installed list and append to the
1016 * response
1017 *
1018 * @param[in] asyncResp Shared pointer to the response message
1019 * @param[in] certURL Path of the certificate object
1020 * @param[in] path Path of the D-Bus service object
1021 * @return None
1022 */
1023 void getCertificateLocations(std::shared_ptr<AsyncResp> &asyncResp,
1024 const std::string &certURL,
1025 const std::string &path,
1026 const std::string &service)
1027 {
1028 BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL
1029 << " Path=" << path << " service= " << service;
1030 crow::connections::systemBus->async_method_call(
1031 [asyncResp, certURL](const boost::system::error_code ec,
1032 const ManagedObjectType &certs) {
1033 if (ec)
1034 {
1035 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1036 messages::internalError(asyncResp->res);
1037 return;
1038 }
1039 nlohmann::json &links =
1040 asyncResp->res.jsonValue["Links"]["Certificates"];
1041 for (auto &cert : certs)
1042 {
1043 long id = getIDFromURL(cert.first.str);
1044 if (id >= 0)
1045 {
1046 links.push_back(
1047 {{"@odata.id", certURL + std::to_string(id)}});
1048 }
1049 }
1050 asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] =
1051 links.size();
1052 },
1053 service, path, certs::dbusObjManagerIntf, "GetManagedObjects");
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001054 }
1055}; // CertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -06001056
1057/**
1058 * Collection of LDAP certificates
1059 */
1060class LDAPCertificateCollection : public Node
1061{
1062 public:
1063 template <typename CrowApp>
1064 LDAPCertificateCollection(CrowApp &app) :
1065 Node(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1066 {
1067 entityPrivileges = {
1068 {boost::beast::http::verb::get, {{"Login"}}},
1069 {boost::beast::http::verb::head, {{"Login"}}},
1070 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1071 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1072 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1073 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1074 }
1075 void doGet(crow::Response &res, const crow::Request &req,
1076 const std::vector<std::string> &params) override
1077 {
1078 res.jsonValue = {
1079 {"@odata.id", "/redfish/v1/AccountService/LDAP/Certificates"},
1080 {"@odata.type", "#CertificateCollection.CertificateCollection"},
Marri Devender Rao37cce912019-02-20 01:05:22 -06001081 {"Name", "LDAP Certificates Collection"},
1082 {"Description", "A Collection of LDAP certificate instances"}};
1083 auto asyncResp = std::make_shared<AsyncResp>(res);
1084 crow::connections::systemBus->async_method_call(
1085 [asyncResp](const boost::system::error_code ec,
1086 const ManagedObjectType &certs) {
1087 if (ec)
1088 {
1089 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1090 messages::internalError(asyncResp->res);
1091 return;
1092 }
1093 nlohmann::json &members = asyncResp->res.jsonValue["Members"];
1094 members = nlohmann::json::array();
1095 for (const auto &cert : certs)
1096 {
1097 long id = getIDFromURL(cert.first.str);
1098 if (id >= 0)
1099 {
1100 members.push_back(
1101 {{"@odata.id", "/redfish/v1/AccountService/"
1102 "LDAP/Certificates/" +
1103 std::to_string(id)}});
1104 }
1105 }
1106 asyncResp->res.jsonValue["Members@odata.count"] =
1107 members.size();
1108 },
1109 certs::ldapServiceName, certs::ldapObjectPath,
1110 certs::dbusObjManagerIntf, "GetManagedObjects");
1111 }
1112
1113 void doPost(crow::Response &res, const crow::Request &req,
1114 const std::vector<std::string> &params) override
1115 {
Marri Devender Rao37cce912019-02-20 01:05:22 -06001116 auto asyncResp = std::make_shared<AsyncResp>(res);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +02001117 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1118
1119 if (certFileBody.empty())
1120 {
Zbigniew Kurzynskia08752f2019-10-10 18:27:18 +02001121 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1122 messages::unrecognizedRequestBody(asyncResp->res);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +02001123 return;
1124 }
1125
1126 std::shared_ptr<CertificateFile> certFile =
1127 std::make_shared<CertificateFile>(certFileBody);
1128
Marri Devender Rao37cce912019-02-20 01:05:22 -06001129 crow::connections::systemBus->async_method_call(
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001130 [asyncResp, certFile](const boost::system::error_code ec,
1131 const std::string &objectPath) {
Marri Devender Rao37cce912019-02-20 01:05:22 -06001132 if (ec)
1133 {
1134 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1135 messages::internalError(asyncResp->res);
1136 return;
1137 }
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001138 long certId = getIDFromURL(objectPath);
1139 if (certId < 0)
1140 {
1141 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1142 << objectPath;
1143 messages::internalError(asyncResp->res);
1144 return;
1145 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001146 std::string certURL =
1147 "/redfish/v1/AccountService/LDAP/Certificates/" +
1148 std::to_string(certId);
Marri Devender Rao37cce912019-02-20 01:05:22 -06001149 getCertificateProperties(asyncResp, objectPath,
1150 certs::ldapServiceName, certId,
1151 certURL, "LDAP Certificate");
1152 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1153 << certFile->getCertFilePath();
1154 },
1155 certs::ldapServiceName, certs::ldapObjectPath,
1156 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1157 }
1158}; // LDAPCertificateCollection
1159
1160/**
1161 * Certificate resource describes a certificate used to prove the identity
1162 * of a component, account or service.
1163 */
1164class LDAPCertificate : public Node
1165{
1166 public:
1167 template <typename CrowApp>
1168 LDAPCertificate(CrowApp &app) :
1169 Node(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/",
1170 std::string())
1171 {
1172 entityPrivileges = {
1173 {boost::beast::http::verb::get, {{"Login"}}},
1174 {boost::beast::http::verb::head, {{"Login"}}},
1175 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1176 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1177 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1178 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1179 }
1180
1181 void doGet(crow::Response &res, const crow::Request &req,
1182 const std::vector<std::string> &params) override
1183 {
1184 auto asyncResp = std::make_shared<AsyncResp>(res);
1185 long id = getIDFromURL(req.url);
1186 if (id < 0)
1187 {
1188 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1189 messages::internalError(asyncResp->res);
1190 return;
1191 }
1192 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << std::to_string(id);
1193 std::string certURL = "/redfish/v1/AccountService/LDAP/Certificates/" +
1194 std::to_string(id);
1195 std::string objectPath = certs::ldapObjectPath;
1196 objectPath += "/";
1197 objectPath += std::to_string(id);
1198 getCertificateProperties(asyncResp, objectPath, certs::ldapServiceName,
1199 id, certURL, "LDAP Certificate");
1200 }
1201}; // LDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001202/**
1203 * Collection of TrustStoreCertificate certificates
1204 */
1205class TrustStoreCertificateCollection : public Node
1206{
1207 public:
1208 template <typename CrowApp>
1209 TrustStoreCertificateCollection(CrowApp &app) :
1210 Node(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
1211 {
1212 entityPrivileges = {
1213 {boost::beast::http::verb::get, {{"Login"}}},
1214 {boost::beast::http::verb::head, {{"Login"}}},
1215 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1216 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1217 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1218 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1219 }
1220 void doGet(crow::Response &res, const crow::Request &req,
1221 const std::vector<std::string> &params) override
1222 {
1223 res.jsonValue = {
1224 {"@odata.id", "/redfish/v1/Managers/bmc/Truststore/Certificates/"},
1225 {"@odata.type", "#CertificateCollection.CertificateCollection"},
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001226 {"Name", "TrustStore Certificates Collection"},
1227 {"Description",
1228 "A Collection of TrustStore certificate instances"}};
1229 auto asyncResp = std::make_shared<AsyncResp>(res);
1230 crow::connections::systemBus->async_method_call(
1231 [asyncResp](const boost::system::error_code ec,
1232 const ManagedObjectType &certs) {
1233 if (ec)
1234 {
1235 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1236 messages::internalError(asyncResp->res);
1237 return;
1238 }
1239 nlohmann::json &members = asyncResp->res.jsonValue["Members"];
1240 members = nlohmann::json::array();
1241 for (const auto &cert : certs)
1242 {
1243 long id = getIDFromURL(cert.first.str);
1244 if (id >= 0)
1245 {
1246 members.push_back(
1247 {{"@odata.id", "/redfish/v1/Managers/bmc/"
1248 "Truststore/Certificates/" +
1249 std::to_string(id)}});
1250 }
1251 }
1252 asyncResp->res.jsonValue["Members@odata.count"] =
1253 members.size();
1254 },
1255 certs::authorityServiceName, certs::authorityObjectPath,
1256 certs::dbusObjManagerIntf, "GetManagedObjects");
1257 }
1258
1259 void doPost(crow::Response &res, const crow::Request &req,
1260 const std::vector<std::string> &params) override
1261 {
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001262 auto asyncResp = std::make_shared<AsyncResp>(res);
Zbigniew Kurzynskia08752f2019-10-10 18:27:18 +02001263 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1264
1265 if (certFileBody.empty())
1266 {
1267 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1268 messages::unrecognizedRequestBody(asyncResp->res);
1269 return;
1270 }
1271
1272 std::shared_ptr<CertificateFile> certFile =
1273 std::make_shared<CertificateFile>(certFileBody);
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001274 crow::connections::systemBus->async_method_call(
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001275 [asyncResp, certFile](const boost::system::error_code ec,
1276 const std::string &objectPath) {
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001277 if (ec)
1278 {
1279 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1280 messages::internalError(asyncResp->res);
1281 return;
1282 }
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001283 long certId = getIDFromURL(objectPath);
1284 if (certId < 0)
1285 {
1286 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1287 << objectPath;
1288 messages::internalError(asyncResp->res);
1289 return;
1290 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001291 std::string certURL = "/redfish/v1/Managers/bmc/"
1292 "Truststore/Certificates/" +
1293 std::to_string(certId);
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001294
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001295 getCertificateProperties(asyncResp, objectPath,
1296 certs::authorityServiceName, certId,
1297 certURL, "TrustStore Certificate");
1298 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1299 << certFile->getCertFilePath();
1300 },
1301 certs::authorityServiceName, certs::authorityObjectPath,
1302 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1303 }
1304}; // TrustStoreCertificateCollection
1305
1306/**
1307 * Certificate resource describes a certificate used to prove the identity
1308 * of a component, account or service.
1309 */
1310class TrustStoreCertificate : public Node
1311{
1312 public:
1313 template <typename CrowApp>
1314 TrustStoreCertificate(CrowApp &app) :
1315 Node(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/",
1316 std::string())
1317 {
1318 entityPrivileges = {
1319 {boost::beast::http::verb::get, {{"Login"}}},
1320 {boost::beast::http::verb::head, {{"Login"}}},
1321 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1322 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1323 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1324 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1325 }
1326
1327 void doGet(crow::Response &res, const crow::Request &req,
1328 const std::vector<std::string> &params) override
1329 {
1330 auto asyncResp = std::make_shared<AsyncResp>(res);
1331 long id = getIDFromURL(req.url);
1332 if (id < 0)
1333 {
1334 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1335 messages::internalError(asyncResp->res);
1336 return;
1337 }
1338 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID="
1339 << std::to_string(id);
1340 std::string certURL =
1341 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1342 std::to_string(id);
1343 std::string objectPath = certs::authorityObjectPath;
1344 objectPath += "/";
1345 objectPath += std::to_string(id);
1346 getCertificateProperties(asyncResp, objectPath,
1347 certs::authorityServiceName, id, certURL,
1348 "TrustStore Certificate");
1349 }
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001350
1351 void doDelete(crow::Response &res, const crow::Request &req,
1352 const std::vector<std::string> &params) override
1353 {
1354 auto asyncResp = std::make_shared<AsyncResp>(res);
1355
1356 if (params.size() != 1)
1357 {
1358 messages::internalError(asyncResp->res);
1359 return;
1360 }
1361
1362 long id = getIDFromURL(req.url);
1363 if (id < 0)
1364 {
1365 BMCWEB_LOG_ERROR << "Invalid url value: " << req.url;
1366 messages::resourceNotFound(asyncResp->res, "TrustStore Certificate",
1367 std::string(req.url));
1368 return;
1369 }
1370 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doDelete ID="
1371 << std::to_string(id);
1372 std::string certPath = certs::authorityObjectPath;
1373 certPath += "/";
1374 certPath += std::to_string(id);
1375
1376 crow::connections::systemBus->async_method_call(
1377 [asyncResp, id](const boost::system::error_code ec) {
1378 if (ec)
1379 {
1380 messages::resourceNotFound(asyncResp->res,
1381 "TrustStore Certificate",
1382 std::to_string(id));
1383 return;
1384 }
1385 BMCWEB_LOG_INFO << "Certificate deleted";
1386 asyncResp->res.result(boost::beast::http::status::no_content);
1387 },
1388 certs::authorityServiceName, certPath, certs::objDeleteIntf,
1389 "Delete");
1390 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001391}; // TrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001392} // namespace redfish