blob: e15c308ee342fcee9251dd6074772010b606985c [file] [log] [blame]
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001/*
2// Copyright (c) 2018 IBM Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
17
18#include "node.hpp"
19
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +020020#include <boost/convert.hpp>
21#include <boost/convert/strtol.hpp>
Marri Devender Rao5968cae2019-01-21 10:27:12 -060022#include <variant>
23namespace redfish
24{
25namespace certs
26{
27constexpr char const *httpsObjectPath =
28 "/xyz/openbmc_project/certs/server/https";
29constexpr char const *certInstallIntf = "xyz.openbmc_project.Certs.Install";
30constexpr char const *certReplaceIntf = "xyz.openbmc_project.Certs.Replace";
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +020031constexpr char const *objDeleteIntf = "xyz.openbmc_project.Object.Delete";
Marri Devender Rao5968cae2019-01-21 10:27:12 -060032constexpr char const *certPropIntf = "xyz.openbmc_project.Certs.Certificate";
33constexpr char const *dbusPropIntf = "org.freedesktop.DBus.Properties";
34constexpr char const *dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
Marri Devender Rao37cce912019-02-20 01:05:22 -060035constexpr char const *ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap";
36constexpr char const *httpsServiceName =
37 "xyz.openbmc_project.Certs.Manager.Server.Https";
38constexpr char const *ldapServiceName =
39 "xyz.openbmc_project.Certs.Manager.Client.Ldap";
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050040constexpr char const *authorityServiceName =
41 "xyz.openbmc_project.Certs.Manager.Authority.Ldap";
42constexpr char const *authorityObjectPath =
43 "/xyz/openbmc_project/certs/authority/ldap";
Marri Devender Rao5968cae2019-01-21 10:27:12 -060044} // namespace certs
45
46/**
47 * The Certificate schema defines a Certificate Service which represents the
48 * actions available to manage certificates and links to where certificates
49 * are installed.
50 */
51class CertificateService : public Node
52{
53 public:
54 CertificateService(CrowApp &app) :
55 Node(app, "/redfish/v1/CertificateService/")
56 {
57 // TODO: Issue#61 No entries are available for Certificate
58 // sevice at https://www.dmtf.org/standards/redfish
59 // "redfish standard registries". Need to modify after DMTF
60 // publish Privilege details for certificate service
61 entityPrivileges = {
62 {boost::beast::http::verb::get, {{"Login"}}},
63 {boost::beast::http::verb::head, {{"Login"}}},
64 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
65 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
66 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
67 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
68 }
69
70 private:
71 void doGet(crow::Response &res, const crow::Request &req,
72 const std::vector<std::string> &params) override
73 {
74 res.jsonValue = {
75 {"@odata.type", "#CertificateService.v1_0_0.CertificateService"},
76 {"@odata.id", "/redfish/v1/CertificateService"},
77 {"@odata.context",
78 "/redfish/v1/$metadata#CertificateService.CertificateService"},
79 {"Id", "CertificateService"},
80 {"Name", "Certificate Service"},
81 {"Description", "Actions available to manage certificates"}};
82 res.jsonValue["CertificateLocations"] = {
83 {"@odata.id",
84 "/redfish/v1/CertificateService/CertificateLocations"}};
85 res.jsonValue["Actions"]["#CertificateService.ReplaceCertificate"] = {
86 {"target", "/redfish/v1/CertificateService/Actions/"
87 "CertificateService.ReplaceCertificate"},
88 {"CertificateType@Redfish.AllowableValues", {"PEM"}}};
Marri Devender Rao30215812019-03-18 08:59:21 -050089 res.jsonValue["Actions"]["#CertificateService.GenerateCSR"] = {
90 {"target", "/redfish/v1/CertificateService/Actions/"
91 "CertificateService.GenerateCSR"}};
Marri Devender Rao5968cae2019-01-21 10:27:12 -060092 res.end();
93 }
94}; // CertificateService
Marri Devender Rao37cce912019-02-20 01:05:22 -060095
Marri Devender Rao5968cae2019-01-21 10:27:12 -060096/**
97 * @brief Find the ID specified in the URL
98 * Finds the numbers specified after the last "/" in the URL and returns.
99 * @param[in] path URL
100 * @return -1 on failure and number on success
101 */
102long getIDFromURL(const std::string_view url)
103{
104 std::size_t found = url.rfind("/");
105 if (found == std::string::npos)
106 {
107 return -1;
108 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200109
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600110 if ((found + 1) < url.length())
111 {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600112 std::string_view str = url.substr(found + 1);
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200113
114 return boost::convert<long>(str, boost::cnv::strtol()).value_or(-1);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600115 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200116
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600117 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 {
Marri Devender Rao30215812019-03-18 08:59:21 -0500424 messages::serviceTemporarilyUnavailable(asyncResp->res,
425 std::to_string(TIME_OUT));
426 return;
427 }
428
429 // Make this static so it survives outside this method
430 static boost::asio::steady_timer timeout(*req.ioService);
431 timeout.expires_after(std::chrono::seconds(TIME_OUT));
432 timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
433 csrMatcher = nullptr;
434 if (ec)
435 {
436 // operation_aborted is expected if timer is canceled before
437 // completion.
438 if (ec != boost::asio::error::operation_aborted)
439 {
440 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
441 }
442 return;
443 }
444 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
445 messages::internalError(asyncResp->res);
446 });
447
448 // create a matcher to wait on CSR object
449 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
450 std::string match("type='signal',"
451 "interface='org.freedesktop.DBus.ObjectManager',"
452 "path='" +
453 objectPath +
454 "',"
455 "member='InterfacesAdded'");
456 csrMatcher = std::make_unique<sdbusplus::bus::match::match>(
457 *crow::connections::systemBus, match,
458 [asyncResp, service, objectPath,
459 certURI](sdbusplus::message::message &m) {
Ed Tanous271584a2019-07-09 16:24:22 -0700460 timeout.cancel();
Marri Devender Rao30215812019-03-18 08:59:21 -0500461 if (m.is_method_error())
462 {
463 BMCWEB_LOG_ERROR << "Dbus method error!!!";
464 messages::internalError(asyncResp->res);
465 return;
466 }
467 std::vector<std::pair<
468 std::string, std::vector<std::pair<
469 std::string, std::variant<std::string>>>>>
470 interfacesProperties;
471 sdbusplus::message::object_path csrObjectPath;
472 m.read(csrObjectPath, interfacesProperties);
473 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
474 for (auto &interface : interfacesProperties)
475 {
476 if (interface.first == "xyz.openbmc_project.Certs.CSR")
477 {
478 getCSR(asyncResp, certURI, service, objectPath,
479 csrObjectPath.str);
480 break;
481 }
482 }
483 });
484 crow::connections::systemBus->async_method_call(
Ed Tanous271584a2019-07-09 16:24:22 -0700485 [asyncResp](const boost::system::error_code &ec,
Marri Devender Rao30215812019-03-18 08:59:21 -0500486 const std::string &path) {
487 if (ec)
488 {
489 BMCWEB_LOG_ERROR << "DBUS response error: " << ec.message();
490 messages::internalError(asyncResp->res);
491 return;
492 }
493 },
494 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
495 "GenerateCSR", *optAlternativeNames, *optChallengePassword, city,
496 commonName, *optContactPerson, country, *optEmail, *optGivenName,
497 *optInitials, *optKeyBitLength, *optKeyCurveId,
498 *optKeyPairAlgorithm, *optKeyUsage, organization,
499 organizationalUnit, state, *optSurname, *optUnstructuredName);
500 }
501}; // CertificateActionGenerateCSR
502
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600503/**
504 * @brief Parse and update Certficate Issue/Subject property
505 *
506 * @param[in] asyncResp Shared pointer to the response message
507 * @param[in] str Issuer/Subject value in key=value pairs
508 * @param[in] type Issuer/Subject
509 * @return None
510 */
511static void updateCertIssuerOrSubject(nlohmann::json &out,
512 const std::string_view value)
513{
514 // example: O=openbmc-project.xyz,CN=localhost
515 std::string_view::iterator i = value.begin();
516 while (i != value.end())
517 {
518 std::string_view::iterator tokenBegin = i;
519 while (i != value.end() && *i != '=')
520 {
521 i++;
522 }
523 if (i == value.end())
524 {
525 break;
526 }
Ed Tanous271584a2019-07-09 16:24:22 -0700527 const std::string_view key(tokenBegin,
528 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600529 i++;
530 tokenBegin = i;
531 while (i != value.end() && *i != ',')
532 {
533 i++;
534 }
Ed Tanous271584a2019-07-09 16:24:22 -0700535 const std::string_view val(tokenBegin,
536 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600537 if (key == "L")
538 {
539 out["City"] = val;
540 }
541 else if (key == "CN")
542 {
543 out["CommonName"] = val;
544 }
545 else if (key == "C")
546 {
547 out["Country"] = val;
548 }
549 else if (key == "O")
550 {
551 out["Organization"] = val;
552 }
553 else if (key == "OU")
554 {
555 out["OrganizationalUnit"] = val;
556 }
557 else if (key == "ST")
558 {
559 out["State"] = val;
560 }
561 // skip comma character
562 if (i != value.end())
563 {
564 i++;
565 }
566 }
567}
568
569/**
570 * @brief Retrieve the certificates properties and append to the response
571 * message
572 *
573 * @param[in] asyncResp Shared pointer to the response message
574 * @param[in] objectPath Path of the D-Bus service object
575 * @param[in] certId Id of the certificate
576 * @param[in] certURL URL of the certificate object
577 * @param[in] name name of the certificate
578 * @return None
579 */
580static void getCertificateProperties(
581 const std::shared_ptr<AsyncResp> &asyncResp, const std::string &objectPath,
Marri Devender Rao37cce912019-02-20 01:05:22 -0600582 const std::string &service, long certId, const std::string &certURL,
583 const std::string &name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600584{
585 using PropertyType =
586 std::variant<std::string, uint64_t, std::vector<std::string>>;
587 using PropertiesMap = boost::container::flat_map<std::string, PropertyType>;
588 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
589 << " certId=" << certId << " certURl=" << certURL;
590 crow::connections::systemBus->async_method_call(
Marri Devender Rao37cce912019-02-20 01:05:22 -0600591 [asyncResp, certURL, certId, name](const boost::system::error_code ec,
592 const PropertiesMap &properties) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600593 if (ec)
594 {
595 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500596 messages::resourceNotFound(asyncResp->res, name,
597 std::to_string(certId));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600598 return;
599 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600600 asyncResp->res.jsonValue = {
601 {"@odata.id", certURL},
602 {"@odata.type", "#Certificate.v1_0_0.Certificate"},
603 {"@odata.context",
604 "/redfish/v1/$metadata#Certificate.Certificate"},
605 {"Id", std::to_string(certId)},
606 {"Name", name},
607 {"Description", name}};
608 for (const auto &property : properties)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600609 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600610 if (property.first == "CertificateString")
611 {
612 asyncResp->res.jsonValue["CertificateString"] = "";
613 const std::string *value =
614 std::get_if<std::string>(&property.second);
615 if (value)
616 {
617 asyncResp->res.jsonValue["CertificateString"] = *value;
618 }
619 }
620 else if (property.first == "KeyUsage")
621 {
622 nlohmann::json &keyUsage =
623 asyncResp->res.jsonValue["KeyUsage"];
624 keyUsage = nlohmann::json::array();
625 const std::vector<std::string> *value =
626 std::get_if<std::vector<std::string>>(&property.second);
627 if (value)
628 {
629 for (const std::string &usage : *value)
630 {
631 keyUsage.push_back(usage);
632 }
633 }
634 }
635 else if (property.first == "Issuer")
636 {
637 const std::string *value =
638 std::get_if<std::string>(&property.second);
639 if (value)
640 {
641 updateCertIssuerOrSubject(
642 asyncResp->res.jsonValue["Issuer"], *value);
643 }
644 }
645 else if (property.first == "Subject")
646 {
647 const std::string *value =
648 std::get_if<std::string>(&property.second);
649 if (value)
650 {
651 updateCertIssuerOrSubject(
652 asyncResp->res.jsonValue["Subject"], *value);
653 }
654 }
655 else if (property.first == "ValidNotAfter")
656 {
657 const uint64_t *value =
658 std::get_if<uint64_t>(&property.second);
659 if (value)
660 {
661 std::time_t time = static_cast<std::time_t>(*value);
662 asyncResp->res.jsonValue["ValidNotAfter"] =
663 crow::utility::getDateTime(time);
664 }
665 }
666 else if (property.first == "ValidNotBefore")
667 {
668 const uint64_t *value =
669 std::get_if<uint64_t>(&property.second);
670 if (value)
671 {
672 std::time_t time = static_cast<std::time_t>(*value);
673 asyncResp->res.jsonValue["ValidNotBefore"] =
674 crow::utility::getDateTime(time);
675 }
676 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600677 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600678 asyncResp->res.addHeader("Location", certURL);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600679 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600680 service, objectPath, certs::dbusPropIntf, "GetAll",
681 certs::certPropIntf);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600682}
683
684using GetObjectType =
685 std::vector<std::pair<std::string, std::vector<std::string>>>;
686
687/**
688 * Action to replace an existing certificate
689 */
690class CertificateActionsReplaceCertificate : public Node
691{
692 public:
693 CertificateActionsReplaceCertificate(CrowApp &app) :
694 Node(app, "/redfish/v1/CertificateService/Actions/"
695 "CertificateService.ReplaceCertificate/")
696 {
697 entityPrivileges = {
698 {boost::beast::http::verb::get, {{"Login"}}},
699 {boost::beast::http::verb::head, {{"Login"}}},
700 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
701 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
702 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
703 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
704 }
705
706 private:
707 void doPost(crow::Response &res, const crow::Request &req,
708 const std::vector<std::string> &params) override
709 {
710 std::string certificate;
711 nlohmann::json certificateUri;
712 std::optional<std::string> certificateType = "PEM";
713 auto asyncResp = std::make_shared<AsyncResp>(res);
714 if (!json_util::readJson(req, asyncResp->res, "CertificateString",
715 certificate, "CertificateUri", certificateUri,
716 "CertificateType", certificateType))
717 {
718 BMCWEB_LOG_ERROR << "Required parameters are missing";
719 messages::internalError(asyncResp->res);
720 return;
721 }
722
723 if (!certificateType)
724 {
725 // should never happen, but it never hurts to be paranoid.
726 return;
727 }
728 if (certificateType != "PEM")
729 {
730 messages::actionParameterNotSupported(
731 asyncResp->res, "CertificateType", "ReplaceCertificate");
732 return;
733 }
734
735 std::string certURI;
736 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
737 "@odata.id", certURI))
738 {
739 messages::actionParameterMissing(
740 asyncResp->res, "ReplaceCertificate", "CertificateUri");
741 return;
742 }
743
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600744 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
745 long id = getIDFromURL(certURI);
746 if (id < 0)
747 {
748 messages::actionParameterValueFormatError(asyncResp->res, certURI,
749 "CertificateUri",
750 "ReplaceCertificate");
751 return;
752 }
753 std::string objectPath;
754 std::string name;
Marri Devender Rao37cce912019-02-20 01:05:22 -0600755 std::string service;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600756 if (boost::starts_with(
757 certURI,
758 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"))
759 {
760 objectPath =
761 std::string(certs::httpsObjectPath) + "/" + std::to_string(id);
762 name = "HTTPS certificate";
Marri Devender Rao37cce912019-02-20 01:05:22 -0600763 service = certs::httpsServiceName;
764 }
765 else if (boost::starts_with(
766 certURI, "/redfish/v1/AccountService/LDAP/Certificates/"))
767 {
768 objectPath =
769 std::string(certs::ldapObjectPath) + "/" + std::to_string(id);
770 name = "LDAP certificate";
771 service = certs::ldapServiceName;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600772 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -0500773 else if (boost::starts_with(
774 certURI,
775 "/redfish/v1/Managers/bmc/Truststore/Certificates/"))
776 {
777 objectPath = std::string(certs::authorityObjectPath) + "/" +
778 std::to_string(id);
779 name = "TrustStore certificate";
780 service = certs::authorityServiceName;
781 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600782 else
783 {
784 messages::actionParameterNotSupported(
785 asyncResp->res, "CertificateUri", "ReplaceCertificate");
786 return;
787 }
788
789 std::shared_ptr<CertificateFile> certFile =
790 std::make_shared<CertificateFile>(certificate);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600791 crow::connections::systemBus->async_method_call(
Marri Devender Rao37cce912019-02-20 01:05:22 -0600792 [asyncResp, certFile, objectPath, service, certURI, id,
793 name](const boost::system::error_code ec) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600794 if (ec)
795 {
796 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500797 messages::resourceNotFound(asyncResp->res, name,
798 std::to_string(id));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600799 return;
800 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600801 getCertificateProperties(asyncResp, objectPath, service, id,
802 certURI, name);
803 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
804 << certFile->getCertFilePath();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600805 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600806 service, objectPath, certs::certReplaceIntf, "Replace",
807 certFile->getCertFilePath());
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600808 }
809}; // CertificateActionsReplaceCertificate
810
811/**
812 * Certificate resource describes a certificate used to prove the identity
813 * of a component, account or service.
814 */
815class HTTPSCertificate : public Node
816{
817 public:
818 template <typename CrowApp>
819 HTTPSCertificate(CrowApp &app) :
820 Node(app,
821 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"
822 "<str>/",
823 std::string())
824 {
825 entityPrivileges = {
826 {boost::beast::http::verb::get, {{"Login"}}},
827 {boost::beast::http::verb::head, {{"Login"}}},
828 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
829 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
830 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
831 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
832 }
833
834 void doGet(crow::Response &res, const crow::Request &req,
835 const std::vector<std::string> &params) override
836 {
837 auto asyncResp = std::make_shared<AsyncResp>(res);
838 if (params.size() != 1)
839 {
840 messages::internalError(asyncResp->res);
841 return;
842 }
843 long id = getIDFromURL(req.url);
844
845 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID=" << std::to_string(id);
846 std::string certURL =
847 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
848 std::to_string(id);
849 std::string objectPath = certs::httpsObjectPath;
850 objectPath += "/";
851 objectPath += std::to_string(id);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600852 getCertificateProperties(asyncResp, objectPath, certs::httpsServiceName,
853 id, certURL, "HTTPS Certificate");
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600854 }
855
856}; // namespace redfish
857
858/**
859 * Collection of HTTPS certificates
860 */
861class HTTPSCertificateCollection : public Node
862{
863 public:
864 template <typename CrowApp>
865 HTTPSCertificateCollection(CrowApp &app) :
866 Node(app,
867 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
868 {
869 entityPrivileges = {
870 {boost::beast::http::verb::get, {{"Login"}}},
871 {boost::beast::http::verb::head, {{"Login"}}},
872 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
873 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
874 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
875 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
876 }
877 void doGet(crow::Response &res, const crow::Request &req,
878 const std::vector<std::string> &params) override
879 {
880 res.jsonValue = {
881 {"@odata.id",
882 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"},
883 {"@odata.type", "#CertificateCollection.CertificateCollection"},
884 {"@odata.context",
885 "/redfish/v1/"
886 "$metadata#CertificateCollection.CertificateCollection"},
887 {"Name", "HTTPS Certificates Collection"},
888 {"Description", "A Collection of HTTPS certificate instances"}};
889 auto asyncResp = std::make_shared<AsyncResp>(res);
890 crow::connections::systemBus->async_method_call(
891 [asyncResp](const boost::system::error_code ec,
Marri Devender Rao37cce912019-02-20 01:05:22 -0600892 const ManagedObjectType &certs) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600893 if (ec)
894 {
895 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
896 messages::internalError(asyncResp->res);
897 return;
898 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600899 nlohmann::json &members = asyncResp->res.jsonValue["Members"];
900 members = nlohmann::json::array();
901 for (const auto &cert : certs)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600902 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600903 long id = getIDFromURL(cert.first.str);
904 if (id >= 0)
905 {
906 members.push_back(
907 {{"@odata.id",
908 "/redfish/v1/Managers/bmc/"
909 "NetworkProtocol/HTTPS/Certificates/" +
910 std::to_string(id)}});
911 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600912 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600913 asyncResp->res.jsonValue["Members@odata.count"] =
914 members.size();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600915 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600916 certs::httpsServiceName, certs::httpsObjectPath,
917 certs::dbusObjManagerIntf, "GetManagedObjects");
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600918 }
919
920 void doPost(crow::Response &res, const crow::Request &req,
921 const std::vector<std::string> &params) override
922 {
923 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
924 auto asyncResp = std::make_shared<AsyncResp>(res);
925 asyncResp->res.jsonValue = {{"Name", "HTTPS Certificate"},
926 {"Description", "HTTPS Certificate"}};
927
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200928 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
929
930 if (certFileBody.empty())
931 {
Zbigniew Kurzynskia08752f2019-10-10 18:27:18 +0200932 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
933 messages::unrecognizedRequestBody(asyncResp->res);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200934 return;
935 }
936
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600937 std::shared_ptr<CertificateFile> certFile =
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200938 std::make_shared<CertificateFile>(certFileBody);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600939
940 crow::connections::systemBus->async_method_call(
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200941 [asyncResp, certFile](const boost::system::error_code ec,
942 const std::string &objectPath) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600943 if (ec)
944 {
945 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
946 messages::internalError(asyncResp->res);
947 return;
948 }
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200949 long certId = getIDFromURL(objectPath);
950 if (certId < 0)
951 {
952 BMCWEB_LOG_ERROR << "Invalid objectPath value"
953 << objectPath;
954 messages::internalError(asyncResp->res);
955 return;
956 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600957 std::string certURL =
958 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/"
959 "Certificates/" +
960 std::to_string(certId);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600961 getCertificateProperties(asyncResp, objectPath,
962 certs::httpsServiceName, certId,
963 certURL, "HTTPS Certificate");
964 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
965 << certFile->getCertFilePath();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600966 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600967 certs::httpsServiceName, certs::httpsObjectPath,
968 certs::certInstallIntf, "Install", certFile->getCertFilePath());
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600969 }
970}; // HTTPSCertificateCollection
971
972/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600973 * The certificate location schema defines a resource that an administrator
974 * can use in order to locate all certificates installed on a given service.
975 */
976class CertificateLocations : public Node
977{
978 public:
979 template <typename CrowApp>
980 CertificateLocations(CrowApp &app) :
981 Node(app, "/redfish/v1/CertificateService/CertificateLocations/")
982 {
983 entityPrivileges = {
984 {boost::beast::http::verb::get, {{"Login"}}},
985 {boost::beast::http::verb::head, {{"Login"}}},
986 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
987 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
988 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
989 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
990 }
991
992 private:
993 void doGet(crow::Response &res, const crow::Request &req,
994 const std::vector<std::string> &params) override
995 {
996 res.jsonValue = {
997 {"@odata.id",
998 "/redfish/v1/CertificateService/CertificateLocations"},
999 {"@odata.type",
1000 "#CertificateLocations.v1_0_0.CertificateLocations"},
1001 {"@odata.context",
1002 "/redfish/v1/$metadata#CertificateLocations.CertificateLocations"},
1003 {"Name", "Certificate Locations"},
1004 {"Id", "CertificateLocations"},
1005 {"Description",
1006 "Defines a resource that an administrator can use in order to "
1007 "locate all certificates installed on a given service"}};
1008 auto asyncResp = std::make_shared<AsyncResp>(res);
1009 nlohmann::json &links =
1010 asyncResp->res.jsonValue["Links"]["Certificates"];
1011 links = nlohmann::json::array();
1012 getCertificateLocations(
1013 asyncResp,
1014 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/",
Marri Devender Rao37cce912019-02-20 01:05:22 -06001015 certs::httpsObjectPath, certs::httpsServiceName);
1016 getCertificateLocations(asyncResp,
1017 "/redfish/v1/AccountService/LDAP/Certificates/",
1018 certs::ldapObjectPath, certs::ldapServiceName);
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001019 getCertificateLocations(
1020 asyncResp, "/redfish/v1/Managers/bmc/Truststore/Certificates/",
1021 certs::authorityObjectPath, certs::authorityServiceName);
Marri Devender Rao37cce912019-02-20 01:05:22 -06001022 }
1023 /**
1024 * @brief Retrieve the certificates installed list and append to the
1025 * response
1026 *
1027 * @param[in] asyncResp Shared pointer to the response message
1028 * @param[in] certURL Path of the certificate object
1029 * @param[in] path Path of the D-Bus service object
1030 * @return None
1031 */
1032 void getCertificateLocations(std::shared_ptr<AsyncResp> &asyncResp,
1033 const std::string &certURL,
1034 const std::string &path,
1035 const std::string &service)
1036 {
1037 BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL
1038 << " Path=" << path << " service= " << service;
1039 crow::connections::systemBus->async_method_call(
1040 [asyncResp, certURL](const boost::system::error_code ec,
1041 const ManagedObjectType &certs) {
1042 if (ec)
1043 {
1044 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1045 messages::internalError(asyncResp->res);
1046 return;
1047 }
1048 nlohmann::json &links =
1049 asyncResp->res.jsonValue["Links"]["Certificates"];
1050 for (auto &cert : certs)
1051 {
1052 long id = getIDFromURL(cert.first.str);
1053 if (id >= 0)
1054 {
1055 links.push_back(
1056 {{"@odata.id", certURL + std::to_string(id)}});
1057 }
1058 }
1059 asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] =
1060 links.size();
1061 },
1062 service, path, certs::dbusObjManagerIntf, "GetManagedObjects");
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001063 }
1064}; // CertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -06001065
1066/**
1067 * Collection of LDAP certificates
1068 */
1069class LDAPCertificateCollection : public Node
1070{
1071 public:
1072 template <typename CrowApp>
1073 LDAPCertificateCollection(CrowApp &app) :
1074 Node(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1075 {
1076 entityPrivileges = {
1077 {boost::beast::http::verb::get, {{"Login"}}},
1078 {boost::beast::http::verb::head, {{"Login"}}},
1079 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1080 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1081 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1082 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1083 }
1084 void doGet(crow::Response &res, const crow::Request &req,
1085 const std::vector<std::string> &params) override
1086 {
1087 res.jsonValue = {
1088 {"@odata.id", "/redfish/v1/AccountService/LDAP/Certificates"},
1089 {"@odata.type", "#CertificateCollection.CertificateCollection"},
1090 {"@odata.context",
1091 "/redfish/v1/"
1092 "$metadata#CertificateCollection.CertificateCollection"},
1093 {"Name", "LDAP Certificates Collection"},
1094 {"Description", "A Collection of LDAP certificate instances"}};
1095 auto asyncResp = std::make_shared<AsyncResp>(res);
1096 crow::connections::systemBus->async_method_call(
1097 [asyncResp](const boost::system::error_code ec,
1098 const ManagedObjectType &certs) {
1099 if (ec)
1100 {
1101 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1102 messages::internalError(asyncResp->res);
1103 return;
1104 }
1105 nlohmann::json &members = asyncResp->res.jsonValue["Members"];
1106 members = nlohmann::json::array();
1107 for (const auto &cert : certs)
1108 {
1109 long id = getIDFromURL(cert.first.str);
1110 if (id >= 0)
1111 {
1112 members.push_back(
1113 {{"@odata.id", "/redfish/v1/AccountService/"
1114 "LDAP/Certificates/" +
1115 std::to_string(id)}});
1116 }
1117 }
1118 asyncResp->res.jsonValue["Members@odata.count"] =
1119 members.size();
1120 },
1121 certs::ldapServiceName, certs::ldapObjectPath,
1122 certs::dbusObjManagerIntf, "GetManagedObjects");
1123 }
1124
1125 void doPost(crow::Response &res, const crow::Request &req,
1126 const std::vector<std::string> &params) override
1127 {
Marri Devender Rao37cce912019-02-20 01:05:22 -06001128 auto asyncResp = std::make_shared<AsyncResp>(res);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +02001129 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1130
1131 if (certFileBody.empty())
1132 {
Zbigniew Kurzynskia08752f2019-10-10 18:27:18 +02001133 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1134 messages::unrecognizedRequestBody(asyncResp->res);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +02001135 return;
1136 }
1137
1138 std::shared_ptr<CertificateFile> certFile =
1139 std::make_shared<CertificateFile>(certFileBody);
1140
Marri Devender Rao37cce912019-02-20 01:05:22 -06001141 crow::connections::systemBus->async_method_call(
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001142 [asyncResp, certFile](const boost::system::error_code ec,
1143 const std::string &objectPath) {
Marri Devender Rao37cce912019-02-20 01:05:22 -06001144 if (ec)
1145 {
1146 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1147 messages::internalError(asyncResp->res);
1148 return;
1149 }
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001150 long certId = getIDFromURL(objectPath);
1151 if (certId < 0)
1152 {
1153 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1154 << objectPath;
1155 messages::internalError(asyncResp->res);
1156 return;
1157 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001158 std::string certURL =
1159 "/redfish/v1/AccountService/LDAP/Certificates/" +
1160 std::to_string(certId);
Marri Devender Rao37cce912019-02-20 01:05:22 -06001161 getCertificateProperties(asyncResp, objectPath,
1162 certs::ldapServiceName, certId,
1163 certURL, "LDAP Certificate");
1164 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1165 << certFile->getCertFilePath();
1166 },
1167 certs::ldapServiceName, certs::ldapObjectPath,
1168 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1169 }
1170}; // LDAPCertificateCollection
1171
1172/**
1173 * Certificate resource describes a certificate used to prove the identity
1174 * of a component, account or service.
1175 */
1176class LDAPCertificate : public Node
1177{
1178 public:
1179 template <typename CrowApp>
1180 LDAPCertificate(CrowApp &app) :
1181 Node(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/",
1182 std::string())
1183 {
1184 entityPrivileges = {
1185 {boost::beast::http::verb::get, {{"Login"}}},
1186 {boost::beast::http::verb::head, {{"Login"}}},
1187 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1188 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1189 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1190 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1191 }
1192
1193 void doGet(crow::Response &res, const crow::Request &req,
1194 const std::vector<std::string> &params) override
1195 {
1196 auto asyncResp = std::make_shared<AsyncResp>(res);
1197 long id = getIDFromURL(req.url);
1198 if (id < 0)
1199 {
1200 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1201 messages::internalError(asyncResp->res);
1202 return;
1203 }
1204 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << std::to_string(id);
1205 std::string certURL = "/redfish/v1/AccountService/LDAP/Certificates/" +
1206 std::to_string(id);
1207 std::string objectPath = certs::ldapObjectPath;
1208 objectPath += "/";
1209 objectPath += std::to_string(id);
1210 getCertificateProperties(asyncResp, objectPath, certs::ldapServiceName,
1211 id, certURL, "LDAP Certificate");
1212 }
1213}; // LDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001214/**
1215 * Collection of TrustStoreCertificate certificates
1216 */
1217class TrustStoreCertificateCollection : public Node
1218{
1219 public:
1220 template <typename CrowApp>
1221 TrustStoreCertificateCollection(CrowApp &app) :
1222 Node(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
1223 {
1224 entityPrivileges = {
1225 {boost::beast::http::verb::get, {{"Login"}}},
1226 {boost::beast::http::verb::head, {{"Login"}}},
1227 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1228 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1229 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1230 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1231 }
1232 void doGet(crow::Response &res, const crow::Request &req,
1233 const std::vector<std::string> &params) override
1234 {
1235 res.jsonValue = {
1236 {"@odata.id", "/redfish/v1/Managers/bmc/Truststore/Certificates/"},
1237 {"@odata.type", "#CertificateCollection.CertificateCollection"},
1238 {"@odata.context",
1239 "/redfish/v1/"
1240 "$metadata#CertificateCollection.CertificateCollection"},
1241 {"Name", "TrustStore Certificates Collection"},
1242 {"Description",
1243 "A Collection of TrustStore certificate instances"}};
1244 auto asyncResp = std::make_shared<AsyncResp>(res);
1245 crow::connections::systemBus->async_method_call(
1246 [asyncResp](const boost::system::error_code ec,
1247 const ManagedObjectType &certs) {
1248 if (ec)
1249 {
1250 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1251 messages::internalError(asyncResp->res);
1252 return;
1253 }
1254 nlohmann::json &members = asyncResp->res.jsonValue["Members"];
1255 members = nlohmann::json::array();
1256 for (const auto &cert : certs)
1257 {
1258 long id = getIDFromURL(cert.first.str);
1259 if (id >= 0)
1260 {
1261 members.push_back(
1262 {{"@odata.id", "/redfish/v1/Managers/bmc/"
1263 "Truststore/Certificates/" +
1264 std::to_string(id)}});
1265 }
1266 }
1267 asyncResp->res.jsonValue["Members@odata.count"] =
1268 members.size();
1269 },
1270 certs::authorityServiceName, certs::authorityObjectPath,
1271 certs::dbusObjManagerIntf, "GetManagedObjects");
1272 }
1273
1274 void doPost(crow::Response &res, const crow::Request &req,
1275 const std::vector<std::string> &params) override
1276 {
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001277 auto asyncResp = std::make_shared<AsyncResp>(res);
Zbigniew Kurzynskia08752f2019-10-10 18:27:18 +02001278 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1279
1280 if (certFileBody.empty())
1281 {
1282 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1283 messages::unrecognizedRequestBody(asyncResp->res);
1284 return;
1285 }
1286
1287 std::shared_ptr<CertificateFile> certFile =
1288 std::make_shared<CertificateFile>(certFileBody);
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001289 crow::connections::systemBus->async_method_call(
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001290 [asyncResp, certFile](const boost::system::error_code ec,
1291 const std::string &objectPath) {
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001292 if (ec)
1293 {
1294 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1295 messages::internalError(asyncResp->res);
1296 return;
1297 }
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001298 long certId = getIDFromURL(objectPath);
1299 if (certId < 0)
1300 {
1301 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1302 << objectPath;
1303 messages::internalError(asyncResp->res);
1304 return;
1305 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001306 std::string certURL = "/redfish/v1/Managers/bmc/"
1307 "Truststore/Certificates/" +
1308 std::to_string(certId);
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001309
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001310 getCertificateProperties(asyncResp, objectPath,
1311 certs::authorityServiceName, certId,
1312 certURL, "TrustStore Certificate");
1313 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1314 << certFile->getCertFilePath();
1315 },
1316 certs::authorityServiceName, certs::authorityObjectPath,
1317 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1318 }
1319}; // TrustStoreCertificateCollection
1320
1321/**
1322 * Certificate resource describes a certificate used to prove the identity
1323 * of a component, account or service.
1324 */
1325class TrustStoreCertificate : public Node
1326{
1327 public:
1328 template <typename CrowApp>
1329 TrustStoreCertificate(CrowApp &app) :
1330 Node(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/",
1331 std::string())
1332 {
1333 entityPrivileges = {
1334 {boost::beast::http::verb::get, {{"Login"}}},
1335 {boost::beast::http::verb::head, {{"Login"}}},
1336 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1337 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1338 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1339 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1340 }
1341
1342 void doGet(crow::Response &res, const crow::Request &req,
1343 const std::vector<std::string> &params) override
1344 {
1345 auto asyncResp = std::make_shared<AsyncResp>(res);
1346 long id = getIDFromURL(req.url);
1347 if (id < 0)
1348 {
1349 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1350 messages::internalError(asyncResp->res);
1351 return;
1352 }
1353 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID="
1354 << std::to_string(id);
1355 std::string certURL =
1356 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1357 std::to_string(id);
1358 std::string objectPath = certs::authorityObjectPath;
1359 objectPath += "/";
1360 objectPath += std::to_string(id);
1361 getCertificateProperties(asyncResp, objectPath,
1362 certs::authorityServiceName, id, certURL,
1363 "TrustStore Certificate");
1364 }
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001365
1366 void doDelete(crow::Response &res, const crow::Request &req,
1367 const std::vector<std::string> &params) override
1368 {
1369 auto asyncResp = std::make_shared<AsyncResp>(res);
1370
1371 if (params.size() != 1)
1372 {
1373 messages::internalError(asyncResp->res);
1374 return;
1375 }
1376
1377 long id = getIDFromURL(req.url);
1378 if (id < 0)
1379 {
1380 BMCWEB_LOG_ERROR << "Invalid url value: " << req.url;
1381 messages::resourceNotFound(asyncResp->res, "TrustStore Certificate",
1382 std::string(req.url));
1383 return;
1384 }
1385 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doDelete ID="
1386 << std::to_string(id);
1387 std::string certPath = certs::authorityObjectPath;
1388 certPath += "/";
1389 certPath += std::to_string(id);
1390
1391 crow::connections::systemBus->async_method_call(
1392 [asyncResp, id](const boost::system::error_code ec) {
1393 if (ec)
1394 {
1395 messages::resourceNotFound(asyncResp->res,
1396 "TrustStore Certificate",
1397 std::to_string(id));
1398 return;
1399 }
1400 BMCWEB_LOG_INFO << "Certificate deleted";
1401 asyncResp->res.result(boost::beast::http::status::no_content);
1402 },
1403 certs::authorityServiceName, certPath, certs::objDeleteIntf,
1404 "Delete");
1405 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001406}; // TrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001407} // namespace redfish