blob: b40b1e9f38a30c89b51a09a8db8377013262fd36 [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";
29constexpr char const *certPropIntf = "xyz.openbmc_project.Certs.Certificate";
30constexpr char const *dbusPropIntf = "org.freedesktop.DBus.Properties";
31constexpr char const *dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
Marri Devender Rao37cce912019-02-20 01:05:22 -060032constexpr char const *ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap";
33constexpr char const *httpsServiceName =
34 "xyz.openbmc_project.Certs.Manager.Server.Https";
35constexpr char const *ldapServiceName =
36 "xyz.openbmc_project.Certs.Manager.Client.Ldap";
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050037constexpr char const *authorityServiceName =
38 "xyz.openbmc_project.Certs.Manager.Authority.Ldap";
39constexpr char const *authorityObjectPath =
40 "/xyz/openbmc_project/certs/authority/ldap";
Marri Devender Rao5968cae2019-01-21 10:27:12 -060041} // namespace certs
42
43/**
44 * The Certificate schema defines a Certificate Service which represents the
45 * actions available to manage certificates and links to where certificates
46 * are installed.
47 */
48class CertificateService : public Node
49{
50 public:
51 CertificateService(CrowApp &app) :
52 Node(app, "/redfish/v1/CertificateService/")
53 {
54 // TODO: Issue#61 No entries are available for Certificate
55 // sevice at https://www.dmtf.org/standards/redfish
56 // "redfish standard registries". Need to modify after DMTF
57 // publish Privilege details for certificate service
58 entityPrivileges = {
59 {boost::beast::http::verb::get, {{"Login"}}},
60 {boost::beast::http::verb::head, {{"Login"}}},
61 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
62 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
63 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
64 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
65 }
66
67 private:
68 void doGet(crow::Response &res, const crow::Request &req,
69 const std::vector<std::string> &params) override
70 {
71 res.jsonValue = {
72 {"@odata.type", "#CertificateService.v1_0_0.CertificateService"},
73 {"@odata.id", "/redfish/v1/CertificateService"},
74 {"@odata.context",
75 "/redfish/v1/$metadata#CertificateService.CertificateService"},
76 {"Id", "CertificateService"},
77 {"Name", "Certificate Service"},
78 {"Description", "Actions available to manage certificates"}};
79 res.jsonValue["CertificateLocations"] = {
80 {"@odata.id",
81 "/redfish/v1/CertificateService/CertificateLocations"}};
82 res.jsonValue["Actions"]["#CertificateService.ReplaceCertificate"] = {
83 {"target", "/redfish/v1/CertificateService/Actions/"
84 "CertificateService.ReplaceCertificate"},
85 {"CertificateType@Redfish.AllowableValues", {"PEM"}}};
Marri Devender Rao30215812019-03-18 08:59:21 -050086 res.jsonValue["Actions"]["#CertificateService.GenerateCSR"] = {
87 {"target", "/redfish/v1/CertificateService/Actions/"
88 "CertificateService.GenerateCSR"}};
Marri Devender Rao5968cae2019-01-21 10:27:12 -060089 res.end();
90 }
91}; // CertificateService
Marri Devender Rao37cce912019-02-20 01:05:22 -060092
Marri Devender Rao5968cae2019-01-21 10:27:12 -060093/**
94 * @brief Find the ID specified in the URL
95 * Finds the numbers specified after the last "/" in the URL and returns.
96 * @param[in] path URL
97 * @return -1 on failure and number on success
98 */
99long getIDFromURL(const std::string_view url)
100{
101 std::size_t found = url.rfind("/");
102 if (found == std::string::npos)
103 {
104 return -1;
105 }
106 if ((found + 1) < url.length())
107 {
108 char *endPtr;
109 std::string_view str = url.substr(found + 1);
110 long value = std::strtol(str.data(), &endPtr, 10);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600111 if (endPtr != str.end())
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600112 {
113 return -1;
114 }
115 return value;
116 }
117 return -1;
118}
119
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200120std::string
121 getCertificateFromReqBody(const std::shared_ptr<AsyncResp> &asyncResp,
122 const crow::Request &req)
123{
124 nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false);
125
126 if (reqJson.is_discarded())
127 {
128 // We did not receive JSON request, proceed as it is RAW data
129 return req.body;
130 }
131
132 std::string certificate;
133 std::optional<std::string> certificateType = "PEM";
134
135 if (!json_util::readJson(reqJson, asyncResp->res, "CertificateString",
136 certificate, "CertificateType", certificateType))
137 {
138 BMCWEB_LOG_ERROR << "Required parameters are missing";
139 messages::internalError(asyncResp->res);
140 return std::string();
141 }
142
143 if (*certificateType != "PEM")
144 {
145 messages::propertyValueNotInList(asyncResp->res, *certificateType,
146 "CertificateType");
147 return std::string();
148 }
149
150 return certificate;
151}
152
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600153/**
154 * Class to create a temporary certificate file for uploading to system
155 */
156class CertificateFile
157{
158 public:
159 CertificateFile() = delete;
160 CertificateFile(const CertificateFile &) = delete;
161 CertificateFile &operator=(const CertificateFile &) = delete;
162 CertificateFile(CertificateFile &&) = delete;
163 CertificateFile &operator=(CertificateFile &&) = delete;
164 CertificateFile(const std::string &certString)
165 {
166 char dirTemplate[] = "/tmp/Certs.XXXXXX";
167 char *tempDirectory = mkdtemp(dirTemplate);
168 if (tempDirectory)
169 {
170 certDirectory = tempDirectory;
171 certificateFile = certDirectory / "cert.pem";
172 std::ofstream out(certificateFile, std::ofstream::out |
173 std::ofstream::binary |
174 std::ofstream::trunc);
175 out << certString;
176 out.close();
177 BMCWEB_LOG_DEBUG << "Creating certificate file" << certificateFile;
178 }
179 }
180 ~CertificateFile()
181 {
182 if (std::filesystem::exists(certDirectory))
183 {
184 BMCWEB_LOG_DEBUG << "Removing certificate file" << certificateFile;
185 try
186 {
187 std::filesystem::remove_all(certDirectory);
188 }
189 catch (const std::filesystem::filesystem_error &e)
190 {
191 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
192 << certDirectory;
193 }
194 }
195 }
196 std::string getCertFilePath()
197 {
198 return certificateFile;
199 }
200
201 private:
202 std::filesystem::path certificateFile;
203 std::filesystem::path certDirectory;
204};
205
Marri Devender Rao30215812019-03-18 08:59:21 -0500206static std::unique_ptr<sdbusplus::bus::match::match> csrMatcher;
207/**
208 * @brief Read data from CSR D-bus object and set to response
209 *
210 * @param[in] asyncResp Shared pointer to the response message
211 * @param[in] certURI Link to certifiate collection URI
212 * @param[in] service D-Bus service name
213 * @param[in] certObjPath certificate D-Bus object path
214 * @param[in] csrObjPath CSR D-Bus object path
215 * @return None
216 */
217static void getCSR(const std::shared_ptr<AsyncResp> &asyncResp,
218 const std::string &certURI, const std::string &service,
219 const std::string &certObjPath,
220 const std::string &csrObjPath)
221{
222 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
223 << " CSRObjectPath=" << csrObjPath
224 << " service=" << service;
225 crow::connections::systemBus->async_method_call(
226 [asyncResp, certURI](const boost::system::error_code ec,
227 const std::string &csr) {
228 if (ec)
229 {
230 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
231 messages::internalError(asyncResp->res);
232 return;
233 }
234 if (csr.empty())
235 {
236 BMCWEB_LOG_ERROR << "CSR read is empty";
237 messages::internalError(asyncResp->res);
238 return;
239 }
240 asyncResp->res.jsonValue["CSRString"] = csr;
241 asyncResp->res.jsonValue["CertificateCollection"] = {
242 {"@odata.id", certURI}};
243 },
244 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
245}
246
247/**
248 * Action to Generate CSR
249 */
250class CertificateActionGenerateCSR : public Node
251{
252 public:
253 CertificateActionGenerateCSR(CrowApp &app) :
254 Node(app, "/redfish/v1/CertificateService/Actions/"
255 "CertificateService.GenerateCSR/")
256 {
257 entityPrivileges = {
258 {boost::beast::http::verb::get, {{"Login"}}},
259 {boost::beast::http::verb::head, {{"Login"}}},
260 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
261 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
262 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
263 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
264 }
265
266 private:
267 void doPost(crow::Response &res, const crow::Request &req,
268 const std::vector<std::string> &params) override
269 {
270 static const int RSA_KEY_BIT_LENGTH = 2048;
271 auto asyncResp = std::make_shared<AsyncResp>(res);
272 // Required parameters
273 std::string city;
274 std::string commonName;
275 std::string country;
276 std::string organization;
277 std::string organizationalUnit;
278 std::string state;
279 nlohmann::json certificateCollection;
280
281 // Optional parameters
282 std::optional<std::vector<std::string>> optAlternativeNames =
283 std::vector<std::string>();
284 std::optional<std::string> optContactPerson = "";
285 std::optional<std::string> optChallengePassword = "";
286 std::optional<std::string> optEmail = "";
287 std::optional<std::string> optGivenName = "";
288 std::optional<std::string> optInitials = "";
289 std::optional<int64_t> optKeyBitLength = RSA_KEY_BIT_LENGTH;
290 std::optional<std::string> optKeyCurveId = "prime256v1";
291 std::optional<std::string> optKeyPairAlgorithm = "EC";
292 std::optional<std::vector<std::string>> optKeyUsage =
293 std::vector<std::string>();
294 std::optional<std::string> optSurname = "";
295 std::optional<std::string> optUnstructuredName = "";
296 if (!json_util::readJson(
297 req, asyncResp->res, "City", city, "CommonName", commonName,
298 "ContactPerson", optContactPerson, "Country", country,
299 "Organization", organization, "OrganizationalUnit",
300 organizationalUnit, "State", state, "CertificateCollection",
301 certificateCollection, "AlternativeNames", optAlternativeNames,
302 "ChallengePassword", optChallengePassword, "Email", optEmail,
303 "GivenName", optGivenName, "Initials", optInitials,
304 "KeyBitLength", optKeyBitLength, "KeyCurveId", optKeyCurveId,
305 "KeyPairAlgorithm", optKeyPairAlgorithm, "KeyUsage",
306 optKeyUsage, "Surname", optSurname, "UnstructuredName",
307 optUnstructuredName))
308 {
309 return;
310 }
311
312 // bmcweb has no way to store or decode a private key challenge
313 // password, which will likely cause bmcweb to crash on startup if this
314 // is not set on a post so not allowing the user to set value
315 if (*optChallengePassword != "")
316 {
317 messages::actionParameterNotSupported(asyncResp->res, "GenerateCSR",
318 "ChallengePassword");
319 return;
320 }
321
322 std::string certURI;
323 if (!redfish::json_util::readJson(certificateCollection, asyncResp->res,
324 "@odata.id", certURI))
325 {
326 return;
327 }
328
329 std::string objectPath;
330 std::string service;
331 if (boost::starts_with(
332 certURI,
333 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
334 {
335 objectPath = certs::httpsObjectPath;
336 service = certs::httpsServiceName;
337 }
Marri Devender Rao3b7f0142019-05-17 02:53:23 -0500338 else if (boost::starts_with(
339 certURI, "/redfish/v1/AccountService/LDAP/Certificates"))
340 {
341 objectPath = certs::ldapObjectPath;
342 service = certs::ldapServiceName;
343 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500344 else
345 {
346 messages::actionParameterNotSupported(
347 asyncResp->res, "CertificateCollection", "GenerateCSR");
348 return;
349 }
350
351 // supporting only EC and RSA algorithm
352 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA")
353 {
354 messages::actionParameterNotSupported(
355 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
356 return;
357 }
358
359 // supporting only 2048 key bit length for RSA algorithm due to time
360 // consumed in generating private key
361 if (*optKeyPairAlgorithm == "RSA" &&
362 *optKeyBitLength != RSA_KEY_BIT_LENGTH)
363 {
364 messages::propertyValueNotInList(asyncResp->res,
365 std::to_string(*optKeyBitLength),
366 "KeyBitLength");
367 return;
368 }
369
370 // validate KeyUsage supporting only 1 type based on URL
371 if (boost::starts_with(
372 certURI,
373 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
374 {
375 if (optKeyUsage->size() == 0)
376 {
377 optKeyUsage->push_back("ServerAuthentication");
378 }
379 else if (optKeyUsage->size() == 1)
380 {
381 if ((*optKeyUsage)[0] != "ServerAuthentication")
382 {
383 messages::propertyValueNotInList(
384 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
385 return;
386 }
387 }
388 else
389 {
390 messages::actionParameterNotSupported(
391 asyncResp->res, "KeyUsage", "GenerateCSR");
392 return;
393 }
394 }
Marri Devender Rao3b7f0142019-05-17 02:53:23 -0500395 else if (boost::starts_with(
396 certURI, "/redfish/v1/AccountService/LDAP/Certificates"))
397 {
398 if (optKeyUsage->size() == 0)
399 {
400 optKeyUsage->push_back("ClientAuthentication");
401 }
402 else if (optKeyUsage->size() == 1)
403 {
404 if ((*optKeyUsage)[0] != "ClientAuthentication")
405 {
406 messages::propertyValueNotInList(
407 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
408 return;
409 }
410 }
411 else
412 {
413 messages::actionParameterNotSupported(
414 asyncResp->res, "KeyUsage", "GenerateCSR");
415 return;
416 }
417 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500418
419 // Only allow one CSR matcher at a time so setting retry time-out and
420 // timer expiry to 10 seconds for now.
421 static const int TIME_OUT = 10;
422 if (csrMatcher)
423 {
424 res.addHeader("Retry-After", std::to_string(TIME_OUT));
425 messages::serviceTemporarilyUnavailable(asyncResp->res,
426 std::to_string(TIME_OUT));
427 return;
428 }
429
430 // Make this static so it survives outside this method
431 static boost::asio::steady_timer timeout(*req.ioService);
432 timeout.expires_after(std::chrono::seconds(TIME_OUT));
433 timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
434 csrMatcher = nullptr;
435 if (ec)
436 {
437 // operation_aborted is expected if timer is canceled before
438 // completion.
439 if (ec != boost::asio::error::operation_aborted)
440 {
441 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
442 }
443 return;
444 }
445 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
446 messages::internalError(asyncResp->res);
447 });
448
449 // create a matcher to wait on CSR object
450 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
451 std::string match("type='signal',"
452 "interface='org.freedesktop.DBus.ObjectManager',"
453 "path='" +
454 objectPath +
455 "',"
456 "member='InterfacesAdded'");
457 csrMatcher = std::make_unique<sdbusplus::bus::match::match>(
458 *crow::connections::systemBus, match,
459 [asyncResp, service, objectPath,
460 certURI](sdbusplus::message::message &m) {
Ed Tanous271584a2019-07-09 16:24:22 -0700461 timeout.cancel();
Marri Devender Rao30215812019-03-18 08:59:21 -0500462 if (m.is_method_error())
463 {
464 BMCWEB_LOG_ERROR << "Dbus method error!!!";
465 messages::internalError(asyncResp->res);
466 return;
467 }
468 std::vector<std::pair<
469 std::string, std::vector<std::pair<
470 std::string, std::variant<std::string>>>>>
471 interfacesProperties;
472 sdbusplus::message::object_path csrObjectPath;
473 m.read(csrObjectPath, interfacesProperties);
474 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
475 for (auto &interface : interfacesProperties)
476 {
477 if (interface.first == "xyz.openbmc_project.Certs.CSR")
478 {
479 getCSR(asyncResp, certURI, service, objectPath,
480 csrObjectPath.str);
481 break;
482 }
483 }
484 });
485 crow::connections::systemBus->async_method_call(
Ed Tanous271584a2019-07-09 16:24:22 -0700486 [asyncResp](const boost::system::error_code &ec,
Marri Devender Rao30215812019-03-18 08:59:21 -0500487 const std::string &path) {
488 if (ec)
489 {
490 BMCWEB_LOG_ERROR << "DBUS response error: " << ec.message();
491 messages::internalError(asyncResp->res);
492 return;
493 }
494 },
495 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
496 "GenerateCSR", *optAlternativeNames, *optChallengePassword, city,
497 commonName, *optContactPerson, country, *optEmail, *optGivenName,
498 *optInitials, *optKeyBitLength, *optKeyCurveId,
499 *optKeyPairAlgorithm, *optKeyUsage, organization,
500 organizationalUnit, state, *optSurname, *optUnstructuredName);
501 }
502}; // CertificateActionGenerateCSR
503
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600504/**
505 * @brief Parse and update Certficate Issue/Subject property
506 *
507 * @param[in] asyncResp Shared pointer to the response message
508 * @param[in] str Issuer/Subject value in key=value pairs
509 * @param[in] type Issuer/Subject
510 * @return None
511 */
512static void updateCertIssuerOrSubject(nlohmann::json &out,
513 const std::string_view value)
514{
515 // example: O=openbmc-project.xyz,CN=localhost
516 std::string_view::iterator i = value.begin();
517 while (i != value.end())
518 {
519 std::string_view::iterator tokenBegin = i;
520 while (i != value.end() && *i != '=')
521 {
522 i++;
523 }
524 if (i == value.end())
525 {
526 break;
527 }
Ed Tanous271584a2019-07-09 16:24:22 -0700528 const std::string_view key(tokenBegin,
529 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600530 i++;
531 tokenBegin = i;
532 while (i != value.end() && *i != ',')
533 {
534 i++;
535 }
Ed Tanous271584a2019-07-09 16:24:22 -0700536 const std::string_view val(tokenBegin,
537 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600538 if (key == "L")
539 {
540 out["City"] = val;
541 }
542 else if (key == "CN")
543 {
544 out["CommonName"] = val;
545 }
546 else if (key == "C")
547 {
548 out["Country"] = val;
549 }
550 else if (key == "O")
551 {
552 out["Organization"] = val;
553 }
554 else if (key == "OU")
555 {
556 out["OrganizationalUnit"] = val;
557 }
558 else if (key == "ST")
559 {
560 out["State"] = val;
561 }
562 // skip comma character
563 if (i != value.end())
564 {
565 i++;
566 }
567 }
568}
569
570/**
571 * @brief Retrieve the certificates properties and append to the response
572 * message
573 *
574 * @param[in] asyncResp Shared pointer to the response message
575 * @param[in] objectPath Path of the D-Bus service object
576 * @param[in] certId Id of the certificate
577 * @param[in] certURL URL of the certificate object
578 * @param[in] name name of the certificate
579 * @return None
580 */
581static void getCertificateProperties(
582 const std::shared_ptr<AsyncResp> &asyncResp, const std::string &objectPath,
Marri Devender Rao37cce912019-02-20 01:05:22 -0600583 const std::string &service, long certId, const std::string &certURL,
584 const std::string &name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600585{
586 using PropertyType =
587 std::variant<std::string, uint64_t, std::vector<std::string>>;
588 using PropertiesMap = boost::container::flat_map<std::string, PropertyType>;
589 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
590 << " certId=" << certId << " certURl=" << certURL;
591 crow::connections::systemBus->async_method_call(
Marri Devender Rao37cce912019-02-20 01:05:22 -0600592 [asyncResp, certURL, certId, name](const boost::system::error_code ec,
593 const PropertiesMap &properties) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600594 if (ec)
595 {
596 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500597 messages::resourceNotFound(asyncResp->res, name,
598 std::to_string(certId));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600599 return;
600 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600601 asyncResp->res.jsonValue = {
602 {"@odata.id", certURL},
603 {"@odata.type", "#Certificate.v1_0_0.Certificate"},
604 {"@odata.context",
605 "/redfish/v1/$metadata#Certificate.Certificate"},
606 {"Id", std::to_string(certId)},
607 {"Name", name},
608 {"Description", name}};
609 for (const auto &property : properties)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600610 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600611 if (property.first == "CertificateString")
612 {
613 asyncResp->res.jsonValue["CertificateString"] = "";
614 const std::string *value =
615 std::get_if<std::string>(&property.second);
616 if (value)
617 {
618 asyncResp->res.jsonValue["CertificateString"] = *value;
619 }
620 }
621 else if (property.first == "KeyUsage")
622 {
623 nlohmann::json &keyUsage =
624 asyncResp->res.jsonValue["KeyUsage"];
625 keyUsage = nlohmann::json::array();
626 const std::vector<std::string> *value =
627 std::get_if<std::vector<std::string>>(&property.second);
628 if (value)
629 {
630 for (const std::string &usage : *value)
631 {
632 keyUsage.push_back(usage);
633 }
634 }
635 }
636 else if (property.first == "Issuer")
637 {
638 const std::string *value =
639 std::get_if<std::string>(&property.second);
640 if (value)
641 {
642 updateCertIssuerOrSubject(
643 asyncResp->res.jsonValue["Issuer"], *value);
644 }
645 }
646 else if (property.first == "Subject")
647 {
648 const std::string *value =
649 std::get_if<std::string>(&property.second);
650 if (value)
651 {
652 updateCertIssuerOrSubject(
653 asyncResp->res.jsonValue["Subject"], *value);
654 }
655 }
656 else if (property.first == "ValidNotAfter")
657 {
658 const uint64_t *value =
659 std::get_if<uint64_t>(&property.second);
660 if (value)
661 {
662 std::time_t time = static_cast<std::time_t>(*value);
663 asyncResp->res.jsonValue["ValidNotAfter"] =
664 crow::utility::getDateTime(time);
665 }
666 }
667 else if (property.first == "ValidNotBefore")
668 {
669 const uint64_t *value =
670 std::get_if<uint64_t>(&property.second);
671 if (value)
672 {
673 std::time_t time = static_cast<std::time_t>(*value);
674 asyncResp->res.jsonValue["ValidNotBefore"] =
675 crow::utility::getDateTime(time);
676 }
677 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600678 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600679 asyncResp->res.addHeader("Location", certURL);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600680 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600681 service, objectPath, certs::dbusPropIntf, "GetAll",
682 certs::certPropIntf);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600683}
684
685using GetObjectType =
686 std::vector<std::pair<std::string, std::vector<std::string>>>;
687
688/**
689 * Action to replace an existing certificate
690 */
691class CertificateActionsReplaceCertificate : public Node
692{
693 public:
694 CertificateActionsReplaceCertificate(CrowApp &app) :
695 Node(app, "/redfish/v1/CertificateService/Actions/"
696 "CertificateService.ReplaceCertificate/")
697 {
698 entityPrivileges = {
699 {boost::beast::http::verb::get, {{"Login"}}},
700 {boost::beast::http::verb::head, {{"Login"}}},
701 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
702 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
703 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
704 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
705 }
706
707 private:
708 void doPost(crow::Response &res, const crow::Request &req,
709 const std::vector<std::string> &params) override
710 {
711 std::string certificate;
712 nlohmann::json certificateUri;
713 std::optional<std::string> certificateType = "PEM";
714 auto asyncResp = std::make_shared<AsyncResp>(res);
715 if (!json_util::readJson(req, asyncResp->res, "CertificateString",
716 certificate, "CertificateUri", certificateUri,
717 "CertificateType", certificateType))
718 {
719 BMCWEB_LOG_ERROR << "Required parameters are missing";
720 messages::internalError(asyncResp->res);
721 return;
722 }
723
724 if (!certificateType)
725 {
726 // should never happen, but it never hurts to be paranoid.
727 return;
728 }
729 if (certificateType != "PEM")
730 {
731 messages::actionParameterNotSupported(
732 asyncResp->res, "CertificateType", "ReplaceCertificate");
733 return;
734 }
735
736 std::string certURI;
737 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
738 "@odata.id", certURI))
739 {
740 messages::actionParameterMissing(
741 asyncResp->res, "ReplaceCertificate", "CertificateUri");
742 return;
743 }
744
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600745 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
746 long id = getIDFromURL(certURI);
747 if (id < 0)
748 {
749 messages::actionParameterValueFormatError(asyncResp->res, certURI,
750 "CertificateUri",
751 "ReplaceCertificate");
752 return;
753 }
754 std::string objectPath;
755 std::string name;
Marri Devender Rao37cce912019-02-20 01:05:22 -0600756 std::string service;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600757 if (boost::starts_with(
758 certURI,
759 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"))
760 {
761 objectPath =
762 std::string(certs::httpsObjectPath) + "/" + std::to_string(id);
763 name = "HTTPS certificate";
Marri Devender Rao37cce912019-02-20 01:05:22 -0600764 service = certs::httpsServiceName;
765 }
766 else if (boost::starts_with(
767 certURI, "/redfish/v1/AccountService/LDAP/Certificates/"))
768 {
769 objectPath =
770 std::string(certs::ldapObjectPath) + "/" + std::to_string(id);
771 name = "LDAP certificate";
772 service = certs::ldapServiceName;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600773 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -0500774 else if (boost::starts_with(
775 certURI,
776 "/redfish/v1/Managers/bmc/Truststore/Certificates/"))
777 {
778 objectPath = std::string(certs::authorityObjectPath) + "/" +
779 std::to_string(id);
780 name = "TrustStore certificate";
781 service = certs::authorityServiceName;
782 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600783 else
784 {
785 messages::actionParameterNotSupported(
786 asyncResp->res, "CertificateUri", "ReplaceCertificate");
787 return;
788 }
789
790 std::shared_ptr<CertificateFile> certFile =
791 std::make_shared<CertificateFile>(certificate);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600792 crow::connections::systemBus->async_method_call(
Marri Devender Rao37cce912019-02-20 01:05:22 -0600793 [asyncResp, certFile, objectPath, service, certURI, id,
794 name](const boost::system::error_code ec) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600795 if (ec)
796 {
797 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500798 messages::resourceNotFound(asyncResp->res, name,
799 std::to_string(id));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600800 return;
801 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600802 getCertificateProperties(asyncResp, objectPath, service, id,
803 certURI, name);
804 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
805 << certFile->getCertFilePath();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600806 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600807 service, objectPath, certs::certReplaceIntf, "Replace",
808 certFile->getCertFilePath());
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600809 }
810}; // CertificateActionsReplaceCertificate
811
812/**
813 * Certificate resource describes a certificate used to prove the identity
814 * of a component, account or service.
815 */
816class HTTPSCertificate : public Node
817{
818 public:
819 template <typename CrowApp>
820 HTTPSCertificate(CrowApp &app) :
821 Node(app,
822 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"
823 "<str>/",
824 std::string())
825 {
826 entityPrivileges = {
827 {boost::beast::http::verb::get, {{"Login"}}},
828 {boost::beast::http::verb::head, {{"Login"}}},
829 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
830 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
831 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
832 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
833 }
834
835 void doGet(crow::Response &res, const crow::Request &req,
836 const std::vector<std::string> &params) override
837 {
838 auto asyncResp = std::make_shared<AsyncResp>(res);
839 if (params.size() != 1)
840 {
841 messages::internalError(asyncResp->res);
842 return;
843 }
844 long id = getIDFromURL(req.url);
845
846 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID=" << std::to_string(id);
847 std::string certURL =
848 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
849 std::to_string(id);
850 std::string objectPath = certs::httpsObjectPath;
851 objectPath += "/";
852 objectPath += std::to_string(id);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600853 getCertificateProperties(asyncResp, objectPath, certs::httpsServiceName,
854 id, certURL, "HTTPS Certificate");
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600855 }
856
857}; // namespace redfish
858
859/**
860 * Collection of HTTPS certificates
861 */
862class HTTPSCertificateCollection : public Node
863{
864 public:
865 template <typename CrowApp>
866 HTTPSCertificateCollection(CrowApp &app) :
867 Node(app,
868 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
869 {
870 entityPrivileges = {
871 {boost::beast::http::verb::get, {{"Login"}}},
872 {boost::beast::http::verb::head, {{"Login"}}},
873 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
874 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
875 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
876 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
877 }
878 void doGet(crow::Response &res, const crow::Request &req,
879 const std::vector<std::string> &params) override
880 {
881 res.jsonValue = {
882 {"@odata.id",
883 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"},
884 {"@odata.type", "#CertificateCollection.CertificateCollection"},
885 {"@odata.context",
886 "/redfish/v1/"
887 "$metadata#CertificateCollection.CertificateCollection"},
888 {"Name", "HTTPS Certificates Collection"},
889 {"Description", "A Collection of HTTPS certificate instances"}};
890 auto asyncResp = std::make_shared<AsyncResp>(res);
891 crow::connections::systemBus->async_method_call(
892 [asyncResp](const boost::system::error_code ec,
Marri Devender Rao37cce912019-02-20 01:05:22 -0600893 const ManagedObjectType &certs) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600894 if (ec)
895 {
896 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
897 messages::internalError(asyncResp->res);
898 return;
899 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600900 nlohmann::json &members = asyncResp->res.jsonValue["Members"];
901 members = nlohmann::json::array();
902 for (const auto &cert : certs)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600903 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600904 long id = getIDFromURL(cert.first.str);
905 if (id >= 0)
906 {
907 members.push_back(
908 {{"@odata.id",
909 "/redfish/v1/Managers/bmc/"
910 "NetworkProtocol/HTTPS/Certificates/" +
911 std::to_string(id)}});
912 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600913 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600914 asyncResp->res.jsonValue["Members@odata.count"] =
915 members.size();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600916 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600917 certs::httpsServiceName, certs::httpsObjectPath,
918 certs::dbusObjManagerIntf, "GetManagedObjects");
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600919 }
920
921 void doPost(crow::Response &res, const crow::Request &req,
922 const std::vector<std::string> &params) override
923 {
924 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
925 auto asyncResp = std::make_shared<AsyncResp>(res);
926 asyncResp->res.jsonValue = {{"Name", "HTTPS Certificate"},
927 {"Description", "HTTPS Certificate"}};
928
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200929 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
930
931 if (certFileBody.empty())
932 {
Zbigniew Kurzynskia08752f2019-10-10 18:27:18 +0200933 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
934 messages::unrecognizedRequestBody(asyncResp->res);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200935 return;
936 }
937
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600938 std::shared_ptr<CertificateFile> certFile =
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200939 std::make_shared<CertificateFile>(certFileBody);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600940
941 crow::connections::systemBus->async_method_call(
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200942 [asyncResp, certFile](const boost::system::error_code ec,
943 const std::string &objectPath) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600944 if (ec)
945 {
946 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
947 messages::internalError(asyncResp->res);
948 return;
949 }
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200950 long certId = getIDFromURL(objectPath);
951 if (certId < 0)
952 {
953 BMCWEB_LOG_ERROR << "Invalid objectPath value"
954 << objectPath;
955 messages::internalError(asyncResp->res);
956 return;
957 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600958 std::string certURL =
959 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/"
960 "Certificates/" +
961 std::to_string(certId);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600962 getCertificateProperties(asyncResp, objectPath,
963 certs::httpsServiceName, certId,
964 certURL, "HTTPS Certificate");
965 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
966 << certFile->getCertFilePath();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600967 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600968 certs::httpsServiceName, certs::httpsObjectPath,
969 certs::certInstallIntf, "Install", certFile->getCertFilePath());
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600970 }
971}; // HTTPSCertificateCollection
972
973/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600974 * The certificate location schema defines a resource that an administrator
975 * can use in order to locate all certificates installed on a given service.
976 */
977class CertificateLocations : public Node
978{
979 public:
980 template <typename CrowApp>
981 CertificateLocations(CrowApp &app) :
982 Node(app, "/redfish/v1/CertificateService/CertificateLocations/")
983 {
984 entityPrivileges = {
985 {boost::beast::http::verb::get, {{"Login"}}},
986 {boost::beast::http::verb::head, {{"Login"}}},
987 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
988 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
989 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
990 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
991 }
992
993 private:
994 void doGet(crow::Response &res, const crow::Request &req,
995 const std::vector<std::string> &params) override
996 {
997 res.jsonValue = {
998 {"@odata.id",
999 "/redfish/v1/CertificateService/CertificateLocations"},
1000 {"@odata.type",
1001 "#CertificateLocations.v1_0_0.CertificateLocations"},
1002 {"@odata.context",
1003 "/redfish/v1/$metadata#CertificateLocations.CertificateLocations"},
1004 {"Name", "Certificate Locations"},
1005 {"Id", "CertificateLocations"},
1006 {"Description",
1007 "Defines a resource that an administrator can use in order to "
1008 "locate all certificates installed on a given service"}};
1009 auto asyncResp = std::make_shared<AsyncResp>(res);
1010 nlohmann::json &links =
1011 asyncResp->res.jsonValue["Links"]["Certificates"];
1012 links = nlohmann::json::array();
1013 getCertificateLocations(
1014 asyncResp,
1015 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/",
Marri Devender Rao37cce912019-02-20 01:05:22 -06001016 certs::httpsObjectPath, certs::httpsServiceName);
1017 getCertificateLocations(asyncResp,
1018 "/redfish/v1/AccountService/LDAP/Certificates/",
1019 certs::ldapObjectPath, certs::ldapServiceName);
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001020 getCertificateLocations(
1021 asyncResp, "/redfish/v1/Managers/bmc/Truststore/Certificates/",
1022 certs::authorityObjectPath, certs::authorityServiceName);
Marri Devender Rao37cce912019-02-20 01:05:22 -06001023 }
1024 /**
1025 * @brief Retrieve the certificates installed list and append to the
1026 * response
1027 *
1028 * @param[in] asyncResp Shared pointer to the response message
1029 * @param[in] certURL Path of the certificate object
1030 * @param[in] path Path of the D-Bus service object
1031 * @return None
1032 */
1033 void getCertificateLocations(std::shared_ptr<AsyncResp> &asyncResp,
1034 const std::string &certURL,
1035 const std::string &path,
1036 const std::string &service)
1037 {
1038 BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL
1039 << " Path=" << path << " service= " << service;
1040 crow::connections::systemBus->async_method_call(
1041 [asyncResp, certURL](const boost::system::error_code ec,
1042 const ManagedObjectType &certs) {
1043 if (ec)
1044 {
1045 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1046 messages::internalError(asyncResp->res);
1047 return;
1048 }
1049 nlohmann::json &links =
1050 asyncResp->res.jsonValue["Links"]["Certificates"];
1051 for (auto &cert : certs)
1052 {
1053 long id = getIDFromURL(cert.first.str);
1054 if (id >= 0)
1055 {
1056 links.push_back(
1057 {{"@odata.id", certURL + std::to_string(id)}});
1058 }
1059 }
1060 asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] =
1061 links.size();
1062 },
1063 service, path, certs::dbusObjManagerIntf, "GetManagedObjects");
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001064 }
1065}; // CertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -06001066
1067/**
1068 * Collection of LDAP certificates
1069 */
1070class LDAPCertificateCollection : public Node
1071{
1072 public:
1073 template <typename CrowApp>
1074 LDAPCertificateCollection(CrowApp &app) :
1075 Node(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1076 {
1077 entityPrivileges = {
1078 {boost::beast::http::verb::get, {{"Login"}}},
1079 {boost::beast::http::verb::head, {{"Login"}}},
1080 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1081 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1082 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1083 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1084 }
1085 void doGet(crow::Response &res, const crow::Request &req,
1086 const std::vector<std::string> &params) override
1087 {
1088 res.jsonValue = {
1089 {"@odata.id", "/redfish/v1/AccountService/LDAP/Certificates"},
1090 {"@odata.type", "#CertificateCollection.CertificateCollection"},
1091 {"@odata.context",
1092 "/redfish/v1/"
1093 "$metadata#CertificateCollection.CertificateCollection"},
1094 {"Name", "LDAP Certificates Collection"},
1095 {"Description", "A Collection of LDAP certificate instances"}};
1096 auto asyncResp = std::make_shared<AsyncResp>(res);
1097 crow::connections::systemBus->async_method_call(
1098 [asyncResp](const boost::system::error_code ec,
1099 const ManagedObjectType &certs) {
1100 if (ec)
1101 {
1102 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1103 messages::internalError(asyncResp->res);
1104 return;
1105 }
1106 nlohmann::json &members = asyncResp->res.jsonValue["Members"];
1107 members = nlohmann::json::array();
1108 for (const auto &cert : certs)
1109 {
1110 long id = getIDFromURL(cert.first.str);
1111 if (id >= 0)
1112 {
1113 members.push_back(
1114 {{"@odata.id", "/redfish/v1/AccountService/"
1115 "LDAP/Certificates/" +
1116 std::to_string(id)}});
1117 }
1118 }
1119 asyncResp->res.jsonValue["Members@odata.count"] =
1120 members.size();
1121 },
1122 certs::ldapServiceName, certs::ldapObjectPath,
1123 certs::dbusObjManagerIntf, "GetManagedObjects");
1124 }
1125
1126 void doPost(crow::Response &res, const crow::Request &req,
1127 const std::vector<std::string> &params) override
1128 {
Marri Devender Rao37cce912019-02-20 01:05:22 -06001129 auto asyncResp = std::make_shared<AsyncResp>(res);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +02001130 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1131
1132 if (certFileBody.empty())
1133 {
Zbigniew Kurzynskia08752f2019-10-10 18:27:18 +02001134 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1135 messages::unrecognizedRequestBody(asyncResp->res);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +02001136 return;
1137 }
1138
1139 std::shared_ptr<CertificateFile> certFile =
1140 std::make_shared<CertificateFile>(certFileBody);
1141
Marri Devender Rao37cce912019-02-20 01:05:22 -06001142 crow::connections::systemBus->async_method_call(
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001143 [asyncResp, certFile](const boost::system::error_code ec,
1144 const std::string &objectPath) {
Marri Devender Rao37cce912019-02-20 01:05:22 -06001145 if (ec)
1146 {
1147 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1148 messages::internalError(asyncResp->res);
1149 return;
1150 }
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001151 long certId = getIDFromURL(objectPath);
1152 if (certId < 0)
1153 {
1154 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1155 << objectPath;
1156 messages::internalError(asyncResp->res);
1157 return;
1158 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001159 std::string certURL =
1160 "/redfish/v1/AccountService/LDAP/Certificates/" +
1161 std::to_string(certId);
Marri Devender Rao37cce912019-02-20 01:05:22 -06001162 getCertificateProperties(asyncResp, objectPath,
1163 certs::ldapServiceName, certId,
1164 certURL, "LDAP Certificate");
1165 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1166 << certFile->getCertFilePath();
1167 },
1168 certs::ldapServiceName, certs::ldapObjectPath,
1169 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1170 }
1171}; // LDAPCertificateCollection
1172
1173/**
1174 * Certificate resource describes a certificate used to prove the identity
1175 * of a component, account or service.
1176 */
1177class LDAPCertificate : public Node
1178{
1179 public:
1180 template <typename CrowApp>
1181 LDAPCertificate(CrowApp &app) :
1182 Node(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/",
1183 std::string())
1184 {
1185 entityPrivileges = {
1186 {boost::beast::http::verb::get, {{"Login"}}},
1187 {boost::beast::http::verb::head, {{"Login"}}},
1188 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1189 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1190 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1191 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1192 }
1193
1194 void doGet(crow::Response &res, const crow::Request &req,
1195 const std::vector<std::string> &params) override
1196 {
1197 auto asyncResp = std::make_shared<AsyncResp>(res);
1198 long id = getIDFromURL(req.url);
1199 if (id < 0)
1200 {
1201 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1202 messages::internalError(asyncResp->res);
1203 return;
1204 }
1205 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << std::to_string(id);
1206 std::string certURL = "/redfish/v1/AccountService/LDAP/Certificates/" +
1207 std::to_string(id);
1208 std::string objectPath = certs::ldapObjectPath;
1209 objectPath += "/";
1210 objectPath += std::to_string(id);
1211 getCertificateProperties(asyncResp, objectPath, certs::ldapServiceName,
1212 id, certURL, "LDAP Certificate");
1213 }
1214}; // LDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001215/**
1216 * Collection of TrustStoreCertificate certificates
1217 */
1218class TrustStoreCertificateCollection : public Node
1219{
1220 public:
1221 template <typename CrowApp>
1222 TrustStoreCertificateCollection(CrowApp &app) :
1223 Node(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
1224 {
1225 entityPrivileges = {
1226 {boost::beast::http::verb::get, {{"Login"}}},
1227 {boost::beast::http::verb::head, {{"Login"}}},
1228 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1229 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1230 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1231 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1232 }
1233 void doGet(crow::Response &res, const crow::Request &req,
1234 const std::vector<std::string> &params) override
1235 {
1236 res.jsonValue = {
1237 {"@odata.id", "/redfish/v1/Managers/bmc/Truststore/Certificates/"},
1238 {"@odata.type", "#CertificateCollection.CertificateCollection"},
1239 {"@odata.context",
1240 "/redfish/v1/"
1241 "$metadata#CertificateCollection.CertificateCollection"},
1242 {"Name", "TrustStore Certificates Collection"},
1243 {"Description",
1244 "A Collection of TrustStore certificate instances"}};
1245 auto asyncResp = std::make_shared<AsyncResp>(res);
1246 crow::connections::systemBus->async_method_call(
1247 [asyncResp](const boost::system::error_code ec,
1248 const ManagedObjectType &certs) {
1249 if (ec)
1250 {
1251 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1252 messages::internalError(asyncResp->res);
1253 return;
1254 }
1255 nlohmann::json &members = asyncResp->res.jsonValue["Members"];
1256 members = nlohmann::json::array();
1257 for (const auto &cert : certs)
1258 {
1259 long id = getIDFromURL(cert.first.str);
1260 if (id >= 0)
1261 {
1262 members.push_back(
1263 {{"@odata.id", "/redfish/v1/Managers/bmc/"
1264 "Truststore/Certificates/" +
1265 std::to_string(id)}});
1266 }
1267 }
1268 asyncResp->res.jsonValue["Members@odata.count"] =
1269 members.size();
1270 },
1271 certs::authorityServiceName, certs::authorityObjectPath,
1272 certs::dbusObjManagerIntf, "GetManagedObjects");
1273 }
1274
1275 void doPost(crow::Response &res, const crow::Request &req,
1276 const std::vector<std::string> &params) override
1277 {
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001278 auto asyncResp = std::make_shared<AsyncResp>(res);
Zbigniew Kurzynskia08752f2019-10-10 18:27:18 +02001279 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1280
1281 if (certFileBody.empty())
1282 {
1283 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1284 messages::unrecognizedRequestBody(asyncResp->res);
1285 return;
1286 }
1287
1288 std::shared_ptr<CertificateFile> certFile =
1289 std::make_shared<CertificateFile>(certFileBody);
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001290 crow::connections::systemBus->async_method_call(
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001291 [asyncResp, certFile](const boost::system::error_code ec,
1292 const std::string &objectPath) {
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001293 if (ec)
1294 {
1295 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1296 messages::internalError(asyncResp->res);
1297 return;
1298 }
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001299 long certId = getIDFromURL(objectPath);
1300 if (certId < 0)
1301 {
1302 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1303 << objectPath;
1304 messages::internalError(asyncResp->res);
1305 return;
1306 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001307 std::string certURL = "/redfish/v1/Managers/bmc/"
1308 "Truststore/Certificates/" +
1309 std::to_string(certId);
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001310
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001311 getCertificateProperties(asyncResp, objectPath,
1312 certs::authorityServiceName, certId,
1313 certURL, "TrustStore Certificate");
1314 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1315 << certFile->getCertFilePath();
1316 },
1317 certs::authorityServiceName, certs::authorityObjectPath,
1318 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1319 }
1320}; // TrustStoreCertificateCollection
1321
1322/**
1323 * Certificate resource describes a certificate used to prove the identity
1324 * of a component, account or service.
1325 */
1326class TrustStoreCertificate : public Node
1327{
1328 public:
1329 template <typename CrowApp>
1330 TrustStoreCertificate(CrowApp &app) :
1331 Node(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/",
1332 std::string())
1333 {
1334 entityPrivileges = {
1335 {boost::beast::http::verb::get, {{"Login"}}},
1336 {boost::beast::http::verb::head, {{"Login"}}},
1337 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1338 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1339 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1340 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1341 }
1342
1343 void doGet(crow::Response &res, const crow::Request &req,
1344 const std::vector<std::string> &params) override
1345 {
1346 auto asyncResp = std::make_shared<AsyncResp>(res);
1347 long id = getIDFromURL(req.url);
1348 if (id < 0)
1349 {
1350 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1351 messages::internalError(asyncResp->res);
1352 return;
1353 }
1354 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID="
1355 << std::to_string(id);
1356 std::string certURL =
1357 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1358 std::to_string(id);
1359 std::string objectPath = certs::authorityObjectPath;
1360 objectPath += "/";
1361 objectPath += std::to_string(id);
1362 getCertificateProperties(asyncResp, objectPath,
1363 certs::authorityServiceName, id, certURL,
1364 "TrustStore Certificate");
1365 }
1366}; // TrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001367} // namespace redfish