blob: 9b4f60ec622468c6f9cce0e087d6fb2315d21bba [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
20#include <variant>
21namespace redfish
22{
23namespace certs
24{
25constexpr char const *httpsObjectPath =
26 "/xyz/openbmc_project/certs/server/https";
27constexpr char const *certInstallIntf = "xyz.openbmc_project.Certs.Install";
28constexpr char const *certReplaceIntf = "xyz.openbmc_project.Certs.Replace";
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +020029constexpr char const *objDeleteIntf = "xyz.openbmc_project.Object.Delete";
Marri Devender Rao5968cae2019-01-21 10:27:12 -060030constexpr char const *certPropIntf = "xyz.openbmc_project.Certs.Certificate";
31constexpr char const *dbusPropIntf = "org.freedesktop.DBus.Properties";
32constexpr char const *dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
Marri Devender Rao37cce912019-02-20 01:05:22 -060033constexpr char const *ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap";
34constexpr char const *httpsServiceName =
35 "xyz.openbmc_project.Certs.Manager.Server.Https";
36constexpr char const *ldapServiceName =
37 "xyz.openbmc_project.Certs.Manager.Client.Ldap";
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050038constexpr char const *authorityServiceName =
39 "xyz.openbmc_project.Certs.Manager.Authority.Ldap";
40constexpr char const *authorityObjectPath =
41 "/xyz/openbmc_project/certs/authority/ldap";
Marri Devender Rao5968cae2019-01-21 10:27:12 -060042} // namespace certs
43
44/**
45 * The Certificate schema defines a Certificate Service which represents the
46 * actions available to manage certificates and links to where certificates
47 * are installed.
48 */
49class CertificateService : public Node
50{
51 public:
52 CertificateService(CrowApp &app) :
53 Node(app, "/redfish/v1/CertificateService/")
54 {
55 // TODO: Issue#61 No entries are available for Certificate
56 // sevice at https://www.dmtf.org/standards/redfish
57 // "redfish standard registries". Need to modify after DMTF
58 // publish Privilege details for certificate service
59 entityPrivileges = {
60 {boost::beast::http::verb::get, {{"Login"}}},
61 {boost::beast::http::verb::head, {{"Login"}}},
62 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
63 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
64 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
65 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
66 }
67
68 private:
69 void doGet(crow::Response &res, const crow::Request &req,
70 const std::vector<std::string> &params) override
71 {
72 res.jsonValue = {
73 {"@odata.type", "#CertificateService.v1_0_0.CertificateService"},
74 {"@odata.id", "/redfish/v1/CertificateService"},
75 {"@odata.context",
76 "/redfish/v1/$metadata#CertificateService.CertificateService"},
77 {"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 }
107 if ((found + 1) < url.length())
108 {
109 char *endPtr;
110 std::string_view str = url.substr(found + 1);
111 long value = std::strtol(str.data(), &endPtr, 10);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600112 if (endPtr != str.end())
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600113 {
114 return -1;
115 }
116 return value;
117 }
118 return -1;
119}
120
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200121std::string
122 getCertificateFromReqBody(const std::shared_ptr<AsyncResp> &asyncResp,
123 const crow::Request &req)
124{
125 nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false);
126
127 if (reqJson.is_discarded())
128 {
129 // We did not receive JSON request, proceed as it is RAW data
130 return req.body;
131 }
132
133 std::string certificate;
134 std::optional<std::string> certificateType = "PEM";
135
136 if (!json_util::readJson(reqJson, asyncResp->res, "CertificateString",
137 certificate, "CertificateType", certificateType))
138 {
139 BMCWEB_LOG_ERROR << "Required parameters are missing";
140 messages::internalError(asyncResp->res);
141 return std::string();
142 }
143
144 if (*certificateType != "PEM")
145 {
146 messages::propertyValueNotInList(asyncResp->res, *certificateType,
147 "CertificateType");
148 return std::string();
149 }
150
151 return certificate;
152}
153
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600154/**
155 * Class to create a temporary certificate file for uploading to system
156 */
157class CertificateFile
158{
159 public:
160 CertificateFile() = delete;
161 CertificateFile(const CertificateFile &) = delete;
162 CertificateFile &operator=(const CertificateFile &) = delete;
163 CertificateFile(CertificateFile &&) = delete;
164 CertificateFile &operator=(CertificateFile &&) = delete;
165 CertificateFile(const std::string &certString)
166 {
167 char dirTemplate[] = "/tmp/Certs.XXXXXX";
168 char *tempDirectory = mkdtemp(dirTemplate);
169 if (tempDirectory)
170 {
171 certDirectory = tempDirectory;
172 certificateFile = certDirectory / "cert.pem";
173 std::ofstream out(certificateFile, std::ofstream::out |
174 std::ofstream::binary |
175 std::ofstream::trunc);
176 out << certString;
177 out.close();
178 BMCWEB_LOG_DEBUG << "Creating certificate file" << certificateFile;
179 }
180 }
181 ~CertificateFile()
182 {
183 if (std::filesystem::exists(certDirectory))
184 {
185 BMCWEB_LOG_DEBUG << "Removing certificate file" << certificateFile;
186 try
187 {
188 std::filesystem::remove_all(certDirectory);
189 }
190 catch (const std::filesystem::filesystem_error &e)
191 {
192 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
193 << certDirectory;
194 }
195 }
196 }
197 std::string getCertFilePath()
198 {
199 return certificateFile;
200 }
201
202 private:
203 std::filesystem::path certificateFile;
204 std::filesystem::path certDirectory;
205};
206
Marri Devender Rao30215812019-03-18 08:59:21 -0500207static std::unique_ptr<sdbusplus::bus::match::match> csrMatcher;
208/**
209 * @brief Read data from CSR D-bus object and set to response
210 *
211 * @param[in] asyncResp Shared pointer to the response message
212 * @param[in] certURI Link to certifiate collection URI
213 * @param[in] service D-Bus service name
214 * @param[in] certObjPath certificate D-Bus object path
215 * @param[in] csrObjPath CSR D-Bus object path
216 * @return None
217 */
218static void getCSR(const std::shared_ptr<AsyncResp> &asyncResp,
219 const std::string &certURI, const std::string &service,
220 const std::string &certObjPath,
221 const std::string &csrObjPath)
222{
223 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
224 << " CSRObjectPath=" << csrObjPath
225 << " service=" << service;
226 crow::connections::systemBus->async_method_call(
227 [asyncResp, certURI](const boost::system::error_code ec,
228 const std::string &csr) {
229 if (ec)
230 {
231 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
232 messages::internalError(asyncResp->res);
233 return;
234 }
235 if (csr.empty())
236 {
237 BMCWEB_LOG_ERROR << "CSR read is empty";
238 messages::internalError(asyncResp->res);
239 return;
240 }
241 asyncResp->res.jsonValue["CSRString"] = csr;
242 asyncResp->res.jsonValue["CertificateCollection"] = {
243 {"@odata.id", certURI}};
244 },
245 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
246}
247
248/**
249 * Action to Generate CSR
250 */
251class CertificateActionGenerateCSR : public Node
252{
253 public:
254 CertificateActionGenerateCSR(CrowApp &app) :
255 Node(app, "/redfish/v1/CertificateService/Actions/"
256 "CertificateService.GenerateCSR/")
257 {
258 entityPrivileges = {
259 {boost::beast::http::verb::get, {{"Login"}}},
260 {boost::beast::http::verb::head, {{"Login"}}},
261 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
262 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
263 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
264 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
265 }
266
267 private:
268 void doPost(crow::Response &res, const crow::Request &req,
269 const std::vector<std::string> &params) override
270 {
271 static const int RSA_KEY_BIT_LENGTH = 2048;
272 auto asyncResp = std::make_shared<AsyncResp>(res);
273 // Required parameters
274 std::string city;
275 std::string commonName;
276 std::string country;
277 std::string organization;
278 std::string organizationalUnit;
279 std::string state;
280 nlohmann::json certificateCollection;
281
282 // Optional parameters
283 std::optional<std::vector<std::string>> optAlternativeNames =
284 std::vector<std::string>();
285 std::optional<std::string> optContactPerson = "";
286 std::optional<std::string> optChallengePassword = "";
287 std::optional<std::string> optEmail = "";
288 std::optional<std::string> optGivenName = "";
289 std::optional<std::string> optInitials = "";
290 std::optional<int64_t> optKeyBitLength = RSA_KEY_BIT_LENGTH;
291 std::optional<std::string> optKeyCurveId = "prime256v1";
292 std::optional<std::string> optKeyPairAlgorithm = "EC";
293 std::optional<std::vector<std::string>> optKeyUsage =
294 std::vector<std::string>();
295 std::optional<std::string> optSurname = "";
296 std::optional<std::string> optUnstructuredName = "";
297 if (!json_util::readJson(
298 req, asyncResp->res, "City", city, "CommonName", commonName,
299 "ContactPerson", optContactPerson, "Country", country,
300 "Organization", organization, "OrganizationalUnit",
301 organizationalUnit, "State", state, "CertificateCollection",
302 certificateCollection, "AlternativeNames", optAlternativeNames,
303 "ChallengePassword", optChallengePassword, "Email", optEmail,
304 "GivenName", optGivenName, "Initials", optInitials,
305 "KeyBitLength", optKeyBitLength, "KeyCurveId", optKeyCurveId,
306 "KeyPairAlgorithm", optKeyPairAlgorithm, "KeyUsage",
307 optKeyUsage, "Surname", optSurname, "UnstructuredName",
308 optUnstructuredName))
309 {
310 return;
311 }
312
313 // bmcweb has no way to store or decode a private key challenge
314 // password, which will likely cause bmcweb to crash on startup if this
315 // is not set on a post so not allowing the user to set value
316 if (*optChallengePassword != "")
317 {
318 messages::actionParameterNotSupported(asyncResp->res, "GenerateCSR",
319 "ChallengePassword");
320 return;
321 }
322
323 std::string certURI;
324 if (!redfish::json_util::readJson(certificateCollection, asyncResp->res,
325 "@odata.id", certURI))
326 {
327 return;
328 }
329
330 std::string objectPath;
331 std::string service;
332 if (boost::starts_with(
333 certURI,
334 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
335 {
336 objectPath = certs::httpsObjectPath;
337 service = certs::httpsServiceName;
338 }
Marri Devender Rao3b7f0142019-05-17 02:53:23 -0500339 else if (boost::starts_with(
340 certURI, "/redfish/v1/AccountService/LDAP/Certificates"))
341 {
342 objectPath = certs::ldapObjectPath;
343 service = certs::ldapServiceName;
344 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500345 else
346 {
347 messages::actionParameterNotSupported(
348 asyncResp->res, "CertificateCollection", "GenerateCSR");
349 return;
350 }
351
352 // supporting only EC and RSA algorithm
353 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA")
354 {
355 messages::actionParameterNotSupported(
356 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
357 return;
358 }
359
360 // supporting only 2048 key bit length for RSA algorithm due to time
361 // consumed in generating private key
362 if (*optKeyPairAlgorithm == "RSA" &&
363 *optKeyBitLength != RSA_KEY_BIT_LENGTH)
364 {
365 messages::propertyValueNotInList(asyncResp->res,
366 std::to_string(*optKeyBitLength),
367 "KeyBitLength");
368 return;
369 }
370
371 // validate KeyUsage supporting only 1 type based on URL
372 if (boost::starts_with(
373 certURI,
374 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
375 {
376 if (optKeyUsage->size() == 0)
377 {
378 optKeyUsage->push_back("ServerAuthentication");
379 }
380 else if (optKeyUsage->size() == 1)
381 {
382 if ((*optKeyUsage)[0] != "ServerAuthentication")
383 {
384 messages::propertyValueNotInList(
385 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
386 return;
387 }
388 }
389 else
390 {
391 messages::actionParameterNotSupported(
392 asyncResp->res, "KeyUsage", "GenerateCSR");
393 return;
394 }
395 }
Marri Devender Rao3b7f0142019-05-17 02:53:23 -0500396 else if (boost::starts_with(
397 certURI, "/redfish/v1/AccountService/LDAP/Certificates"))
398 {
399 if (optKeyUsage->size() == 0)
400 {
401 optKeyUsage->push_back("ClientAuthentication");
402 }
403 else if (optKeyUsage->size() == 1)
404 {
405 if ((*optKeyUsage)[0] != "ClientAuthentication")
406 {
407 messages::propertyValueNotInList(
408 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
409 return;
410 }
411 }
412 else
413 {
414 messages::actionParameterNotSupported(
415 asyncResp->res, "KeyUsage", "GenerateCSR");
416 return;
417 }
418 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500419
420 // Only allow one CSR matcher at a time so setting retry time-out and
421 // timer expiry to 10 seconds for now.
422 static const int TIME_OUT = 10;
423 if (csrMatcher)
424 {
425 res.addHeader("Retry-After", std::to_string(TIME_OUT));
426 messages::serviceTemporarilyUnavailable(asyncResp->res,
427 std::to_string(TIME_OUT));
428 return;
429 }
430
431 // Make this static so it survives outside this method
432 static boost::asio::steady_timer timeout(*req.ioService);
433 timeout.expires_after(std::chrono::seconds(TIME_OUT));
434 timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
435 csrMatcher = nullptr;
436 if (ec)
437 {
438 // operation_aborted is expected if timer is canceled before
439 // completion.
440 if (ec != boost::asio::error::operation_aborted)
441 {
442 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
443 }
444 return;
445 }
446 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
447 messages::internalError(asyncResp->res);
448 });
449
450 // create a matcher to wait on CSR object
451 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
452 std::string match("type='signal',"
453 "interface='org.freedesktop.DBus.ObjectManager',"
454 "path='" +
455 objectPath +
456 "',"
457 "member='InterfacesAdded'");
458 csrMatcher = std::make_unique<sdbusplus::bus::match::match>(
459 *crow::connections::systemBus, match,
460 [asyncResp, service, objectPath,
461 certURI](sdbusplus::message::message &m) {
Ed Tanous271584a2019-07-09 16:24:22 -0700462 timeout.cancel();
Marri Devender Rao30215812019-03-18 08:59:21 -0500463 if (m.is_method_error())
464 {
465 BMCWEB_LOG_ERROR << "Dbus method error!!!";
466 messages::internalError(asyncResp->res);
467 return;
468 }
469 std::vector<std::pair<
470 std::string, std::vector<std::pair<
471 std::string, std::variant<std::string>>>>>
472 interfacesProperties;
473 sdbusplus::message::object_path csrObjectPath;
474 m.read(csrObjectPath, interfacesProperties);
475 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
476 for (auto &interface : interfacesProperties)
477 {
478 if (interface.first == "xyz.openbmc_project.Certs.CSR")
479 {
480 getCSR(asyncResp, certURI, service, objectPath,
481 csrObjectPath.str);
482 break;
483 }
484 }
485 });
486 crow::connections::systemBus->async_method_call(
Ed Tanous271584a2019-07-09 16:24:22 -0700487 [asyncResp](const boost::system::error_code &ec,
Marri Devender Rao30215812019-03-18 08:59:21 -0500488 const std::string &path) {
489 if (ec)
490 {
491 BMCWEB_LOG_ERROR << "DBUS response error: " << ec.message();
492 messages::internalError(asyncResp->res);
493 return;
494 }
495 },
496 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
497 "GenerateCSR", *optAlternativeNames, *optChallengePassword, city,
498 commonName, *optContactPerson, country, *optEmail, *optGivenName,
499 *optInitials, *optKeyBitLength, *optKeyCurveId,
500 *optKeyPairAlgorithm, *optKeyUsage, organization,
501 organizationalUnit, state, *optSurname, *optUnstructuredName);
502 }
503}; // CertificateActionGenerateCSR
504
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600505/**
506 * @brief Parse and update Certficate Issue/Subject property
507 *
508 * @param[in] asyncResp Shared pointer to the response message
509 * @param[in] str Issuer/Subject value in key=value pairs
510 * @param[in] type Issuer/Subject
511 * @return None
512 */
513static void updateCertIssuerOrSubject(nlohmann::json &out,
514 const std::string_view value)
515{
516 // example: O=openbmc-project.xyz,CN=localhost
517 std::string_view::iterator i = value.begin();
518 while (i != value.end())
519 {
520 std::string_view::iterator tokenBegin = i;
521 while (i != value.end() && *i != '=')
522 {
523 i++;
524 }
525 if (i == value.end())
526 {
527 break;
528 }
Ed Tanous271584a2019-07-09 16:24:22 -0700529 const std::string_view key(tokenBegin,
530 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600531 i++;
532 tokenBegin = i;
533 while (i != value.end() && *i != ',')
534 {
535 i++;
536 }
Ed Tanous271584a2019-07-09 16:24:22 -0700537 const std::string_view val(tokenBegin,
538 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600539 if (key == "L")
540 {
541 out["City"] = val;
542 }
543 else if (key == "CN")
544 {
545 out["CommonName"] = val;
546 }
547 else if (key == "C")
548 {
549 out["Country"] = val;
550 }
551 else if (key == "O")
552 {
553 out["Organization"] = val;
554 }
555 else if (key == "OU")
556 {
557 out["OrganizationalUnit"] = val;
558 }
559 else if (key == "ST")
560 {
561 out["State"] = val;
562 }
563 // skip comma character
564 if (i != value.end())
565 {
566 i++;
567 }
568 }
569}
570
571/**
572 * @brief Retrieve the certificates properties and append to the response
573 * message
574 *
575 * @param[in] asyncResp Shared pointer to the response message
576 * @param[in] objectPath Path of the D-Bus service object
577 * @param[in] certId Id of the certificate
578 * @param[in] certURL URL of the certificate object
579 * @param[in] name name of the certificate
580 * @return None
581 */
582static void getCertificateProperties(
583 const std::shared_ptr<AsyncResp> &asyncResp, const std::string &objectPath,
Marri Devender Rao37cce912019-02-20 01:05:22 -0600584 const std::string &service, long certId, const std::string &certURL,
585 const std::string &name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600586{
587 using PropertyType =
588 std::variant<std::string, uint64_t, std::vector<std::string>>;
589 using PropertiesMap = boost::container::flat_map<std::string, PropertyType>;
590 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
591 << " certId=" << certId << " certURl=" << certURL;
592 crow::connections::systemBus->async_method_call(
Marri Devender Rao37cce912019-02-20 01:05:22 -0600593 [asyncResp, certURL, certId, name](const boost::system::error_code ec,
594 const PropertiesMap &properties) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600595 if (ec)
596 {
597 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500598 messages::resourceNotFound(asyncResp->res, name,
599 std::to_string(certId));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600600 return;
601 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600602 asyncResp->res.jsonValue = {
603 {"@odata.id", certURL},
604 {"@odata.type", "#Certificate.v1_0_0.Certificate"},
605 {"@odata.context",
606 "/redfish/v1/$metadata#Certificate.Certificate"},
607 {"Id", std::to_string(certId)},
608 {"Name", name},
609 {"Description", name}};
610 for (const auto &property : properties)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600611 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600612 if (property.first == "CertificateString")
613 {
614 asyncResp->res.jsonValue["CertificateString"] = "";
615 const std::string *value =
616 std::get_if<std::string>(&property.second);
617 if (value)
618 {
619 asyncResp->res.jsonValue["CertificateString"] = *value;
620 }
621 }
622 else if (property.first == "KeyUsage")
623 {
624 nlohmann::json &keyUsage =
625 asyncResp->res.jsonValue["KeyUsage"];
626 keyUsage = nlohmann::json::array();
627 const std::vector<std::string> *value =
628 std::get_if<std::vector<std::string>>(&property.second);
629 if (value)
630 {
631 for (const std::string &usage : *value)
632 {
633 keyUsage.push_back(usage);
634 }
635 }
636 }
637 else if (property.first == "Issuer")
638 {
639 const std::string *value =
640 std::get_if<std::string>(&property.second);
641 if (value)
642 {
643 updateCertIssuerOrSubject(
644 asyncResp->res.jsonValue["Issuer"], *value);
645 }
646 }
647 else if (property.first == "Subject")
648 {
649 const std::string *value =
650 std::get_if<std::string>(&property.second);
651 if (value)
652 {
653 updateCertIssuerOrSubject(
654 asyncResp->res.jsonValue["Subject"], *value);
655 }
656 }
657 else if (property.first == "ValidNotAfter")
658 {
659 const uint64_t *value =
660 std::get_if<uint64_t>(&property.second);
661 if (value)
662 {
663 std::time_t time = static_cast<std::time_t>(*value);
664 asyncResp->res.jsonValue["ValidNotAfter"] =
665 crow::utility::getDateTime(time);
666 }
667 }
668 else if (property.first == "ValidNotBefore")
669 {
670 const uint64_t *value =
671 std::get_if<uint64_t>(&property.second);
672 if (value)
673 {
674 std::time_t time = static_cast<std::time_t>(*value);
675 asyncResp->res.jsonValue["ValidNotBefore"] =
676 crow::utility::getDateTime(time);
677 }
678 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600679 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600680 asyncResp->res.addHeader("Location", certURL);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600681 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600682 service, objectPath, certs::dbusPropIntf, "GetAll",
683 certs::certPropIntf);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600684}
685
686using GetObjectType =
687 std::vector<std::pair<std::string, std::vector<std::string>>>;
688
689/**
690 * Action to replace an existing certificate
691 */
692class CertificateActionsReplaceCertificate : public Node
693{
694 public:
695 CertificateActionsReplaceCertificate(CrowApp &app) :
696 Node(app, "/redfish/v1/CertificateService/Actions/"
697 "CertificateService.ReplaceCertificate/")
698 {
699 entityPrivileges = {
700 {boost::beast::http::verb::get, {{"Login"}}},
701 {boost::beast::http::verb::head, {{"Login"}}},
702 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
703 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
704 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
705 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
706 }
707
708 private:
709 void doPost(crow::Response &res, const crow::Request &req,
710 const std::vector<std::string> &params) override
711 {
712 std::string certificate;
713 nlohmann::json certificateUri;
714 std::optional<std::string> certificateType = "PEM";
715 auto asyncResp = std::make_shared<AsyncResp>(res);
716 if (!json_util::readJson(req, asyncResp->res, "CertificateString",
717 certificate, "CertificateUri", certificateUri,
718 "CertificateType", certificateType))
719 {
720 BMCWEB_LOG_ERROR << "Required parameters are missing";
721 messages::internalError(asyncResp->res);
722 return;
723 }
724
725 if (!certificateType)
726 {
727 // should never happen, but it never hurts to be paranoid.
728 return;
729 }
730 if (certificateType != "PEM")
731 {
732 messages::actionParameterNotSupported(
733 asyncResp->res, "CertificateType", "ReplaceCertificate");
734 return;
735 }
736
737 std::string certURI;
738 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
739 "@odata.id", certURI))
740 {
741 messages::actionParameterMissing(
742 asyncResp->res, "ReplaceCertificate", "CertificateUri");
743 return;
744 }
745
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600746 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
747 long id = getIDFromURL(certURI);
748 if (id < 0)
749 {
750 messages::actionParameterValueFormatError(asyncResp->res, certURI,
751 "CertificateUri",
752 "ReplaceCertificate");
753 return;
754 }
755 std::string objectPath;
756 std::string name;
Marri Devender Rao37cce912019-02-20 01:05:22 -0600757 std::string service;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600758 if (boost::starts_with(
759 certURI,
760 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"))
761 {
762 objectPath =
763 std::string(certs::httpsObjectPath) + "/" + std::to_string(id);
764 name = "HTTPS certificate";
Marri Devender Rao37cce912019-02-20 01:05:22 -0600765 service = certs::httpsServiceName;
766 }
767 else if (boost::starts_with(
768 certURI, "/redfish/v1/AccountService/LDAP/Certificates/"))
769 {
770 objectPath =
771 std::string(certs::ldapObjectPath) + "/" + std::to_string(id);
772 name = "LDAP certificate";
773 service = certs::ldapServiceName;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600774 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -0500775 else if (boost::starts_with(
776 certURI,
777 "/redfish/v1/Managers/bmc/Truststore/Certificates/"))
778 {
779 objectPath = std::string(certs::authorityObjectPath) + "/" +
780 std::to_string(id);
781 name = "TrustStore certificate";
782 service = certs::authorityServiceName;
783 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600784 else
785 {
786 messages::actionParameterNotSupported(
787 asyncResp->res, "CertificateUri", "ReplaceCertificate");
788 return;
789 }
790
791 std::shared_ptr<CertificateFile> certFile =
792 std::make_shared<CertificateFile>(certificate);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600793 crow::connections::systemBus->async_method_call(
Marri Devender Rao37cce912019-02-20 01:05:22 -0600794 [asyncResp, certFile, objectPath, service, certURI, id,
795 name](const boost::system::error_code ec) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600796 if (ec)
797 {
798 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500799 messages::resourceNotFound(asyncResp->res, name,
800 std::to_string(id));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600801 return;
802 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600803 getCertificateProperties(asyncResp, objectPath, service, id,
804 certURI, name);
805 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
806 << certFile->getCertFilePath();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600807 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600808 service, objectPath, certs::certReplaceIntf, "Replace",
809 certFile->getCertFilePath());
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600810 }
811}; // CertificateActionsReplaceCertificate
812
813/**
814 * Certificate resource describes a certificate used to prove the identity
815 * of a component, account or service.
816 */
817class HTTPSCertificate : public Node
818{
819 public:
820 template <typename CrowApp>
821 HTTPSCertificate(CrowApp &app) :
822 Node(app,
823 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"
824 "<str>/",
825 std::string())
826 {
827 entityPrivileges = {
828 {boost::beast::http::verb::get, {{"Login"}}},
829 {boost::beast::http::verb::head, {{"Login"}}},
830 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
831 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
832 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
833 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
834 }
835
836 void doGet(crow::Response &res, const crow::Request &req,
837 const std::vector<std::string> &params) override
838 {
839 auto asyncResp = std::make_shared<AsyncResp>(res);
840 if (params.size() != 1)
841 {
842 messages::internalError(asyncResp->res);
843 return;
844 }
845 long id = getIDFromURL(req.url);
846
847 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID=" << std::to_string(id);
848 std::string certURL =
849 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
850 std::to_string(id);
851 std::string objectPath = certs::httpsObjectPath;
852 objectPath += "/";
853 objectPath += std::to_string(id);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600854 getCertificateProperties(asyncResp, objectPath, certs::httpsServiceName,
855 id, certURL, "HTTPS Certificate");
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600856 }
857
858}; // namespace redfish
859
860/**
861 * Collection of HTTPS certificates
862 */
863class HTTPSCertificateCollection : public Node
864{
865 public:
866 template <typename CrowApp>
867 HTTPSCertificateCollection(CrowApp &app) :
868 Node(app,
869 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
870 {
871 entityPrivileges = {
872 {boost::beast::http::verb::get, {{"Login"}}},
873 {boost::beast::http::verb::head, {{"Login"}}},
874 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
875 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
876 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
877 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
878 }
879 void doGet(crow::Response &res, const crow::Request &req,
880 const std::vector<std::string> &params) override
881 {
882 res.jsonValue = {
883 {"@odata.id",
884 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"},
885 {"@odata.type", "#CertificateCollection.CertificateCollection"},
886 {"@odata.context",
887 "/redfish/v1/"
888 "$metadata#CertificateCollection.CertificateCollection"},
889 {"Name", "HTTPS Certificates Collection"},
890 {"Description", "A Collection of HTTPS certificate instances"}};
891 auto asyncResp = std::make_shared<AsyncResp>(res);
892 crow::connections::systemBus->async_method_call(
893 [asyncResp](const boost::system::error_code ec,
Marri Devender Rao37cce912019-02-20 01:05:22 -0600894 const ManagedObjectType &certs) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600895 if (ec)
896 {
897 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
898 messages::internalError(asyncResp->res);
899 return;
900 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600901 nlohmann::json &members = asyncResp->res.jsonValue["Members"];
902 members = nlohmann::json::array();
903 for (const auto &cert : certs)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600904 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600905 long id = getIDFromURL(cert.first.str);
906 if (id >= 0)
907 {
908 members.push_back(
909 {{"@odata.id",
910 "/redfish/v1/Managers/bmc/"
911 "NetworkProtocol/HTTPS/Certificates/" +
912 std::to_string(id)}});
913 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600914 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600915 asyncResp->res.jsonValue["Members@odata.count"] =
916 members.size();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600917 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600918 certs::httpsServiceName, certs::httpsObjectPath,
919 certs::dbusObjManagerIntf, "GetManagedObjects");
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600920 }
921
922 void doPost(crow::Response &res, const crow::Request &req,
923 const std::vector<std::string> &params) override
924 {
925 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
926 auto asyncResp = std::make_shared<AsyncResp>(res);
927 asyncResp->res.jsonValue = {{"Name", "HTTPS Certificate"},
928 {"Description", "HTTPS Certificate"}};
929
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200930 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
931
932 if (certFileBody.empty())
933 {
Zbigniew Kurzynskia08752f2019-10-10 18:27:18 +0200934 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
935 messages::unrecognizedRequestBody(asyncResp->res);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200936 return;
937 }
938
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600939 std::shared_ptr<CertificateFile> certFile =
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200940 std::make_shared<CertificateFile>(certFileBody);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600941
942 crow::connections::systemBus->async_method_call(
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200943 [asyncResp, certFile](const boost::system::error_code ec,
944 const std::string &objectPath) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600945 if (ec)
946 {
947 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
948 messages::internalError(asyncResp->res);
949 return;
950 }
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200951 long certId = getIDFromURL(objectPath);
952 if (certId < 0)
953 {
954 BMCWEB_LOG_ERROR << "Invalid objectPath value"
955 << objectPath;
956 messages::internalError(asyncResp->res);
957 return;
958 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600959 std::string certURL =
960 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/"
961 "Certificates/" +
962 std::to_string(certId);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600963 getCertificateProperties(asyncResp, objectPath,
964 certs::httpsServiceName, certId,
965 certURL, "HTTPS Certificate");
966 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
967 << certFile->getCertFilePath();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600968 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600969 certs::httpsServiceName, certs::httpsObjectPath,
970 certs::certInstallIntf, "Install", certFile->getCertFilePath());
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600971 }
972}; // HTTPSCertificateCollection
973
974/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600975 * The certificate location schema defines a resource that an administrator
976 * can use in order to locate all certificates installed on a given service.
977 */
978class CertificateLocations : public Node
979{
980 public:
981 template <typename CrowApp>
982 CertificateLocations(CrowApp &app) :
983 Node(app, "/redfish/v1/CertificateService/CertificateLocations/")
984 {
985 entityPrivileges = {
986 {boost::beast::http::verb::get, {{"Login"}}},
987 {boost::beast::http::verb::head, {{"Login"}}},
988 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
989 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
990 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
991 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
992 }
993
994 private:
995 void doGet(crow::Response &res, const crow::Request &req,
996 const std::vector<std::string> &params) override
997 {
998 res.jsonValue = {
999 {"@odata.id",
1000 "/redfish/v1/CertificateService/CertificateLocations"},
1001 {"@odata.type",
1002 "#CertificateLocations.v1_0_0.CertificateLocations"},
1003 {"@odata.context",
1004 "/redfish/v1/$metadata#CertificateLocations.CertificateLocations"},
1005 {"Name", "Certificate Locations"},
1006 {"Id", "CertificateLocations"},
1007 {"Description",
1008 "Defines a resource that an administrator can use in order to "
1009 "locate all certificates installed on a given service"}};
1010 auto asyncResp = std::make_shared<AsyncResp>(res);
1011 nlohmann::json &links =
1012 asyncResp->res.jsonValue["Links"]["Certificates"];
1013 links = nlohmann::json::array();
1014 getCertificateLocations(
1015 asyncResp,
1016 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/",
Marri Devender Rao37cce912019-02-20 01:05:22 -06001017 certs::httpsObjectPath, certs::httpsServiceName);
1018 getCertificateLocations(asyncResp,
1019 "/redfish/v1/AccountService/LDAP/Certificates/",
1020 certs::ldapObjectPath, certs::ldapServiceName);
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001021 getCertificateLocations(
1022 asyncResp, "/redfish/v1/Managers/bmc/Truststore/Certificates/",
1023 certs::authorityObjectPath, certs::authorityServiceName);
Marri Devender Rao37cce912019-02-20 01:05:22 -06001024 }
1025 /**
1026 * @brief Retrieve the certificates installed list and append to the
1027 * response
1028 *
1029 * @param[in] asyncResp Shared pointer to the response message
1030 * @param[in] certURL Path of the certificate object
1031 * @param[in] path Path of the D-Bus service object
1032 * @return None
1033 */
1034 void getCertificateLocations(std::shared_ptr<AsyncResp> &asyncResp,
1035 const std::string &certURL,
1036 const std::string &path,
1037 const std::string &service)
1038 {
1039 BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL
1040 << " Path=" << path << " service= " << service;
1041 crow::connections::systemBus->async_method_call(
1042 [asyncResp, certURL](const boost::system::error_code ec,
1043 const ManagedObjectType &certs) {
1044 if (ec)
1045 {
1046 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1047 messages::internalError(asyncResp->res);
1048 return;
1049 }
1050 nlohmann::json &links =
1051 asyncResp->res.jsonValue["Links"]["Certificates"];
1052 for (auto &cert : certs)
1053 {
1054 long id = getIDFromURL(cert.first.str);
1055 if (id >= 0)
1056 {
1057 links.push_back(
1058 {{"@odata.id", certURL + std::to_string(id)}});
1059 }
1060 }
1061 asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] =
1062 links.size();
1063 },
1064 service, path, certs::dbusObjManagerIntf, "GetManagedObjects");
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001065 }
1066}; // CertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -06001067
1068/**
1069 * Collection of LDAP certificates
1070 */
1071class LDAPCertificateCollection : public Node
1072{
1073 public:
1074 template <typename CrowApp>
1075 LDAPCertificateCollection(CrowApp &app) :
1076 Node(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1077 {
1078 entityPrivileges = {
1079 {boost::beast::http::verb::get, {{"Login"}}},
1080 {boost::beast::http::verb::head, {{"Login"}}},
1081 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1082 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1083 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1084 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1085 }
1086 void doGet(crow::Response &res, const crow::Request &req,
1087 const std::vector<std::string> &params) override
1088 {
1089 res.jsonValue = {
1090 {"@odata.id", "/redfish/v1/AccountService/LDAP/Certificates"},
1091 {"@odata.type", "#CertificateCollection.CertificateCollection"},
1092 {"@odata.context",
1093 "/redfish/v1/"
1094 "$metadata#CertificateCollection.CertificateCollection"},
1095 {"Name", "LDAP Certificates Collection"},
1096 {"Description", "A Collection of LDAP certificate instances"}};
1097 auto asyncResp = std::make_shared<AsyncResp>(res);
1098 crow::connections::systemBus->async_method_call(
1099 [asyncResp](const boost::system::error_code ec,
1100 const ManagedObjectType &certs) {
1101 if (ec)
1102 {
1103 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1104 messages::internalError(asyncResp->res);
1105 return;
1106 }
1107 nlohmann::json &members = asyncResp->res.jsonValue["Members"];
1108 members = nlohmann::json::array();
1109 for (const auto &cert : certs)
1110 {
1111 long id = getIDFromURL(cert.first.str);
1112 if (id >= 0)
1113 {
1114 members.push_back(
1115 {{"@odata.id", "/redfish/v1/AccountService/"
1116 "LDAP/Certificates/" +
1117 std::to_string(id)}});
1118 }
1119 }
1120 asyncResp->res.jsonValue["Members@odata.count"] =
1121 members.size();
1122 },
1123 certs::ldapServiceName, certs::ldapObjectPath,
1124 certs::dbusObjManagerIntf, "GetManagedObjects");
1125 }
1126
1127 void doPost(crow::Response &res, const crow::Request &req,
1128 const std::vector<std::string> &params) override
1129 {
Marri Devender Rao37cce912019-02-20 01:05:22 -06001130 auto asyncResp = std::make_shared<AsyncResp>(res);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +02001131 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1132
1133 if (certFileBody.empty())
1134 {
Zbigniew Kurzynskia08752f2019-10-10 18:27:18 +02001135 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1136 messages::unrecognizedRequestBody(asyncResp->res);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +02001137 return;
1138 }
1139
1140 std::shared_ptr<CertificateFile> certFile =
1141 std::make_shared<CertificateFile>(certFileBody);
1142
Marri Devender Rao37cce912019-02-20 01:05:22 -06001143 crow::connections::systemBus->async_method_call(
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001144 [asyncResp, certFile](const boost::system::error_code ec,
1145 const std::string &objectPath) {
Marri Devender Rao37cce912019-02-20 01:05:22 -06001146 if (ec)
1147 {
1148 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1149 messages::internalError(asyncResp->res);
1150 return;
1151 }
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001152 long certId = getIDFromURL(objectPath);
1153 if (certId < 0)
1154 {
1155 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1156 << objectPath;
1157 messages::internalError(asyncResp->res);
1158 return;
1159 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001160 std::string certURL =
1161 "/redfish/v1/AccountService/LDAP/Certificates/" +
1162 std::to_string(certId);
Marri Devender Rao37cce912019-02-20 01:05:22 -06001163 getCertificateProperties(asyncResp, objectPath,
1164 certs::ldapServiceName, certId,
1165 certURL, "LDAP Certificate");
1166 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1167 << certFile->getCertFilePath();
1168 },
1169 certs::ldapServiceName, certs::ldapObjectPath,
1170 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1171 }
1172}; // LDAPCertificateCollection
1173
1174/**
1175 * Certificate resource describes a certificate used to prove the identity
1176 * of a component, account or service.
1177 */
1178class LDAPCertificate : public Node
1179{
1180 public:
1181 template <typename CrowApp>
1182 LDAPCertificate(CrowApp &app) :
1183 Node(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/",
1184 std::string())
1185 {
1186 entityPrivileges = {
1187 {boost::beast::http::verb::get, {{"Login"}}},
1188 {boost::beast::http::verb::head, {{"Login"}}},
1189 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1190 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1191 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1192 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1193 }
1194
1195 void doGet(crow::Response &res, const crow::Request &req,
1196 const std::vector<std::string> &params) override
1197 {
1198 auto asyncResp = std::make_shared<AsyncResp>(res);
1199 long id = getIDFromURL(req.url);
1200 if (id < 0)
1201 {
1202 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1203 messages::internalError(asyncResp->res);
1204 return;
1205 }
1206 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << std::to_string(id);
1207 std::string certURL = "/redfish/v1/AccountService/LDAP/Certificates/" +
1208 std::to_string(id);
1209 std::string objectPath = certs::ldapObjectPath;
1210 objectPath += "/";
1211 objectPath += std::to_string(id);
1212 getCertificateProperties(asyncResp, objectPath, certs::ldapServiceName,
1213 id, certURL, "LDAP Certificate");
1214 }
1215}; // LDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001216/**
1217 * Collection of TrustStoreCertificate certificates
1218 */
1219class TrustStoreCertificateCollection : public Node
1220{
1221 public:
1222 template <typename CrowApp>
1223 TrustStoreCertificateCollection(CrowApp &app) :
1224 Node(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
1225 {
1226 entityPrivileges = {
1227 {boost::beast::http::verb::get, {{"Login"}}},
1228 {boost::beast::http::verb::head, {{"Login"}}},
1229 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1230 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1231 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1232 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1233 }
1234 void doGet(crow::Response &res, const crow::Request &req,
1235 const std::vector<std::string> &params) override
1236 {
1237 res.jsonValue = {
1238 {"@odata.id", "/redfish/v1/Managers/bmc/Truststore/Certificates/"},
1239 {"@odata.type", "#CertificateCollection.CertificateCollection"},
1240 {"@odata.context",
1241 "/redfish/v1/"
1242 "$metadata#CertificateCollection.CertificateCollection"},
1243 {"Name", "TrustStore Certificates Collection"},
1244 {"Description",
1245 "A Collection of TrustStore certificate instances"}};
1246 auto asyncResp = std::make_shared<AsyncResp>(res);
1247 crow::connections::systemBus->async_method_call(
1248 [asyncResp](const boost::system::error_code ec,
1249 const ManagedObjectType &certs) {
1250 if (ec)
1251 {
1252 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1253 messages::internalError(asyncResp->res);
1254 return;
1255 }
1256 nlohmann::json &members = asyncResp->res.jsonValue["Members"];
1257 members = nlohmann::json::array();
1258 for (const auto &cert : certs)
1259 {
1260 long id = getIDFromURL(cert.first.str);
1261 if (id >= 0)
1262 {
1263 members.push_back(
1264 {{"@odata.id", "/redfish/v1/Managers/bmc/"
1265 "Truststore/Certificates/" +
1266 std::to_string(id)}});
1267 }
1268 }
1269 asyncResp->res.jsonValue["Members@odata.count"] =
1270 members.size();
1271 },
1272 certs::authorityServiceName, certs::authorityObjectPath,
1273 certs::dbusObjManagerIntf, "GetManagedObjects");
1274 }
1275
1276 void doPost(crow::Response &res, const crow::Request &req,
1277 const std::vector<std::string> &params) override
1278 {
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001279 auto asyncResp = std::make_shared<AsyncResp>(res);
Zbigniew Kurzynskia08752f2019-10-10 18:27:18 +02001280 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1281
1282 if (certFileBody.empty())
1283 {
1284 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1285 messages::unrecognizedRequestBody(asyncResp->res);
1286 return;
1287 }
1288
1289 std::shared_ptr<CertificateFile> certFile =
1290 std::make_shared<CertificateFile>(certFileBody);
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001291 crow::connections::systemBus->async_method_call(
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001292 [asyncResp, certFile](const boost::system::error_code ec,
1293 const std::string &objectPath) {
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001294 if (ec)
1295 {
1296 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1297 messages::internalError(asyncResp->res);
1298 return;
1299 }
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001300 long certId = getIDFromURL(objectPath);
1301 if (certId < 0)
1302 {
1303 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1304 << objectPath;
1305 messages::internalError(asyncResp->res);
1306 return;
1307 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001308 std::string certURL = "/redfish/v1/Managers/bmc/"
1309 "Truststore/Certificates/" +
1310 std::to_string(certId);
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001311
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001312 getCertificateProperties(asyncResp, objectPath,
1313 certs::authorityServiceName, certId,
1314 certURL, "TrustStore Certificate");
1315 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1316 << certFile->getCertFilePath();
1317 },
1318 certs::authorityServiceName, certs::authorityObjectPath,
1319 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1320 }
1321}; // TrustStoreCertificateCollection
1322
1323/**
1324 * Certificate resource describes a certificate used to prove the identity
1325 * of a component, account or service.
1326 */
1327class TrustStoreCertificate : public Node
1328{
1329 public:
1330 template <typename CrowApp>
1331 TrustStoreCertificate(CrowApp &app) :
1332 Node(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/",
1333 std::string())
1334 {
1335 entityPrivileges = {
1336 {boost::beast::http::verb::get, {{"Login"}}},
1337 {boost::beast::http::verb::head, {{"Login"}}},
1338 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1339 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1340 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1341 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1342 }
1343
1344 void doGet(crow::Response &res, const crow::Request &req,
1345 const std::vector<std::string> &params) override
1346 {
1347 auto asyncResp = std::make_shared<AsyncResp>(res);
1348 long id = getIDFromURL(req.url);
1349 if (id < 0)
1350 {
1351 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1352 messages::internalError(asyncResp->res);
1353 return;
1354 }
1355 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID="
1356 << std::to_string(id);
1357 std::string certURL =
1358 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1359 std::to_string(id);
1360 std::string objectPath = certs::authorityObjectPath;
1361 objectPath += "/";
1362 objectPath += std::to_string(id);
1363 getCertificateProperties(asyncResp, objectPath,
1364 certs::authorityServiceName, id, certURL,
1365 "TrustStore Certificate");
1366 }
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001367
1368 void doDelete(crow::Response &res, const crow::Request &req,
1369 const std::vector<std::string> &params) override
1370 {
1371 auto asyncResp = std::make_shared<AsyncResp>(res);
1372
1373 if (params.size() != 1)
1374 {
1375 messages::internalError(asyncResp->res);
1376 return;
1377 }
1378
1379 long id = getIDFromURL(req.url);
1380 if (id < 0)
1381 {
1382 BMCWEB_LOG_ERROR << "Invalid url value: " << req.url;
1383 messages::resourceNotFound(asyncResp->res, "TrustStore Certificate",
1384 std::string(req.url));
1385 return;
1386 }
1387 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doDelete ID="
1388 << std::to_string(id);
1389 std::string certPath = certs::authorityObjectPath;
1390 certPath += "/";
1391 certPath += std::to_string(id);
1392
1393 crow::connections::systemBus->async_method_call(
1394 [asyncResp, id](const boost::system::error_code ec) {
1395 if (ec)
1396 {
1397 messages::resourceNotFound(asyncResp->res,
1398 "TrustStore Certificate",
1399 std::to_string(id));
1400 return;
1401 }
1402 BMCWEB_LOG_INFO << "Certificate deleted";
1403 asyncResp->res.result(boost::beast::http::status::no_content);
1404 },
1405 certs::authorityServiceName, certPath, certs::objDeleteIntf,
1406 "Delete");
1407 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001408}; // TrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001409} // namespace redfish