blob: 35c60b17fd474c2a1eb36ef83a8de51515bdba0c [file] [log] [blame]
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001/*
2// Copyright (c) 2018 IBM Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
17
18#include "node.hpp"
19
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +020020#include <boost/convert.hpp>
21#include <boost/convert/strtol.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050022
Marri Devender Rao5968cae2019-01-21 10:27:12 -060023#include <variant>
24namespace redfish
25{
26namespace certs
27{
Gunnar Mills1214b7e2020-06-04 10:11:30 -050028constexpr char const* httpsObjectPath =
Marri Devender Rao5968cae2019-01-21 10:27:12 -060029 "/xyz/openbmc_project/certs/server/https";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050030constexpr char const* certInstallIntf = "xyz.openbmc_project.Certs.Install";
31constexpr char const* certReplaceIntf = "xyz.openbmc_project.Certs.Replace";
32constexpr char const* objDeleteIntf = "xyz.openbmc_project.Object.Delete";
33constexpr char const* certPropIntf = "xyz.openbmc_project.Certs.Certificate";
34constexpr char const* dbusPropIntf = "org.freedesktop.DBus.Properties";
35constexpr char const* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
36constexpr char const* ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap";
37constexpr char const* httpsServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060038 "xyz.openbmc_project.Certs.Manager.Server.Https";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050039constexpr char const* ldapServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060040 "xyz.openbmc_project.Certs.Manager.Client.Ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050041constexpr char const* authorityServiceName =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050042 "xyz.openbmc_project.Certs.Manager.Authority.Ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050043constexpr char const* authorityObjectPath =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050044 "/xyz/openbmc_project/certs/authority/ldap";
Marri Devender Rao5968cae2019-01-21 10:27:12 -060045} // namespace certs
46
47/**
48 * The Certificate schema defines a Certificate Service which represents the
49 * actions available to manage certificates and links to where certificates
50 * are installed.
51 */
52class CertificateService : public Node
53{
54 public:
Ed Tanous52cc1122020-07-18 13:51:21 -070055 CertificateService(App& app) : Node(app, "/redfish/v1/CertificateService/")
Marri Devender Rao5968cae2019-01-21 10:27:12 -060056 {
57 // TODO: Issue#61 No entries are available for Certificate
Gunnar Mills4e0453b2020-07-08 14:00:30 -050058 // service at https://www.dmtf.org/standards/redfish
Marri Devender Rao5968cae2019-01-21 10:27:12 -060059 // "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:
Ed Tanouscb13a392020-07-25 19:02:03 +000071 void doGet(crow::Response& res, const crow::Request&,
72 const std::vector<std::string>&) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -060073 {
74 res.jsonValue = {
75 {"@odata.type", "#CertificateService.v1_0_0.CertificateService"},
76 {"@odata.id", "/redfish/v1/CertificateService"},
Marri Devender Rao5968cae2019-01-21 10:27:12 -060077 {"Id", "CertificateService"},
78 {"Name", "Certificate Service"},
79 {"Description", "Actions available to manage certificates"}};
80 res.jsonValue["CertificateLocations"] = {
81 {"@odata.id",
82 "/redfish/v1/CertificateService/CertificateLocations"}};
83 res.jsonValue["Actions"]["#CertificateService.ReplaceCertificate"] = {
84 {"target", "/redfish/v1/CertificateService/Actions/"
85 "CertificateService.ReplaceCertificate"},
86 {"CertificateType@Redfish.AllowableValues", {"PEM"}}};
Marri Devender Rao30215812019-03-18 08:59:21 -050087 res.jsonValue["Actions"]["#CertificateService.GenerateCSR"] = {
88 {"target", "/redfish/v1/CertificateService/Actions/"
89 "CertificateService.GenerateCSR"}};
Marri Devender Rao5968cae2019-01-21 10:27:12 -060090 res.end();
91 }
92}; // CertificateService
Marri Devender Rao37cce912019-02-20 01:05:22 -060093
Marri Devender Rao5968cae2019-01-21 10:27:12 -060094/**
95 * @brief Find the ID specified in the URL
96 * Finds the numbers specified after the last "/" in the URL and returns.
97 * @param[in] path URL
98 * @return -1 on failure and number on success
99 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000100inline long getIDFromURL(const std::string_view url)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600101{
102 std::size_t found = url.rfind("/");
103 if (found == std::string::npos)
104 {
105 return -1;
106 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200107
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600108 if ((found + 1) < url.length())
109 {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600110 std::string_view str = url.substr(found + 1);
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200111
112 return boost::convert<long>(str, boost::cnv::strtol()).value_or(-1);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600113 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200114
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600115 return -1;
116}
117
Ed Tanous23a21a12020-07-25 04:45:05 +0000118inline std::string
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500119 getCertificateFromReqBody(const std::shared_ptr<AsyncResp>& asyncResp,
120 const crow::Request& req)
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200121{
122 nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false);
123
124 if (reqJson.is_discarded())
125 {
126 // We did not receive JSON request, proceed as it is RAW data
127 return req.body;
128 }
129
130 std::string certificate;
131 std::optional<std::string> certificateType = "PEM";
132
133 if (!json_util::readJson(reqJson, asyncResp->res, "CertificateString",
134 certificate, "CertificateType", certificateType))
135 {
136 BMCWEB_LOG_ERROR << "Required parameters are missing";
137 messages::internalError(asyncResp->res);
138 return std::string();
139 }
140
141 if (*certificateType != "PEM")
142 {
143 messages::propertyValueNotInList(asyncResp->res, *certificateType,
144 "CertificateType");
145 return std::string();
146 }
147
148 return certificate;
149}
150
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600151/**
152 * Class to create a temporary certificate file for uploading to system
153 */
154class CertificateFile
155{
156 public:
157 CertificateFile() = delete;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500158 CertificateFile(const CertificateFile&) = delete;
159 CertificateFile& operator=(const CertificateFile&) = delete;
160 CertificateFile(CertificateFile&&) = delete;
161 CertificateFile& operator=(CertificateFile&&) = delete;
162 CertificateFile(const std::string& certString)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600163 {
164 char dirTemplate[] = "/tmp/Certs.XXXXXX";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500165 char* tempDirectory = mkdtemp(dirTemplate);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600166 if (tempDirectory)
167 {
168 certDirectory = tempDirectory;
169 certificateFile = certDirectory / "cert.pem";
170 std::ofstream out(certificateFile, std::ofstream::out |
171 std::ofstream::binary |
172 std::ofstream::trunc);
173 out << certString;
174 out.close();
175 BMCWEB_LOG_DEBUG << "Creating certificate file" << certificateFile;
176 }
177 }
178 ~CertificateFile()
179 {
180 if (std::filesystem::exists(certDirectory))
181 {
182 BMCWEB_LOG_DEBUG << "Removing certificate file" << certificateFile;
Ed Tanous23a21a12020-07-25 04:45:05 +0000183 std::error_code ec;
184 std::filesystem::remove_all(certDirectory, ec);
185 if (ec)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600186 {
187 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
188 << certDirectory;
189 }
190 }
191 }
192 std::string getCertFilePath()
193 {
194 return certificateFile;
195 }
196
197 private:
198 std::filesystem::path certificateFile;
199 std::filesystem::path certDirectory;
200};
201
Marri Devender Rao30215812019-03-18 08:59:21 -0500202static std::unique_ptr<sdbusplus::bus::match::match> csrMatcher;
203/**
204 * @brief Read data from CSR D-bus object and set to response
205 *
206 * @param[in] asyncResp Shared pointer to the response message
207 * @param[in] certURI Link to certifiate collection URI
208 * @param[in] service D-Bus service name
209 * @param[in] certObjPath certificate D-Bus object path
210 * @param[in] csrObjPath CSR D-Bus object path
211 * @return None
212 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500213static void getCSR(const std::shared_ptr<AsyncResp>& asyncResp,
214 const std::string& certURI, const std::string& service,
215 const std::string& certObjPath,
216 const std::string& csrObjPath)
Marri Devender Rao30215812019-03-18 08:59:21 -0500217{
218 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
219 << " CSRObjectPath=" << csrObjPath
220 << " service=" << service;
221 crow::connections::systemBus->async_method_call(
222 [asyncResp, certURI](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500223 const std::string& csr) {
Marri Devender Rao30215812019-03-18 08:59:21 -0500224 if (ec)
225 {
226 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
227 messages::internalError(asyncResp->res);
228 return;
229 }
230 if (csr.empty())
231 {
232 BMCWEB_LOG_ERROR << "CSR read is empty";
233 messages::internalError(asyncResp->res);
234 return;
235 }
236 asyncResp->res.jsonValue["CSRString"] = csr;
237 asyncResp->res.jsonValue["CertificateCollection"] = {
238 {"@odata.id", certURI}};
239 },
240 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
241}
242
243/**
244 * Action to Generate CSR
245 */
246class CertificateActionGenerateCSR : public Node
247{
248 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700249 CertificateActionGenerateCSR(App& app) :
Marri Devender Rao30215812019-03-18 08:59:21 -0500250 Node(app, "/redfish/v1/CertificateService/Actions/"
251 "CertificateService.GenerateCSR/")
252 {
253 entityPrivileges = {
254 {boost::beast::http::verb::get, {{"Login"}}},
255 {boost::beast::http::verb::head, {{"Login"}}},
256 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
257 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
258 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
259 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
260 }
261
262 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500263 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +0000264 const std::vector<std::string>&) override
Marri Devender Rao30215812019-03-18 08:59:21 -0500265 {
266 static const int RSA_KEY_BIT_LENGTH = 2048;
267 auto asyncResp = std::make_shared<AsyncResp>(res);
268 // Required parameters
269 std::string city;
270 std::string commonName;
271 std::string country;
272 std::string organization;
273 std::string organizationalUnit;
274 std::string state;
275 nlohmann::json certificateCollection;
276
277 // Optional parameters
278 std::optional<std::vector<std::string>> optAlternativeNames =
279 std::vector<std::string>();
280 std::optional<std::string> optContactPerson = "";
281 std::optional<std::string> optChallengePassword = "";
282 std::optional<std::string> optEmail = "";
283 std::optional<std::string> optGivenName = "";
284 std::optional<std::string> optInitials = "";
285 std::optional<int64_t> optKeyBitLength = RSA_KEY_BIT_LENGTH;
Vernon Maueryaaf32062020-03-09 10:41:31 -0700286 std::optional<std::string> optKeyCurveId = "secp384r1";
Marri Devender Rao30215812019-03-18 08:59:21 -0500287 std::optional<std::string> optKeyPairAlgorithm = "EC";
288 std::optional<std::vector<std::string>> optKeyUsage =
289 std::vector<std::string>();
290 std::optional<std::string> optSurname = "";
291 std::optional<std::string> optUnstructuredName = "";
292 if (!json_util::readJson(
293 req, asyncResp->res, "City", city, "CommonName", commonName,
294 "ContactPerson", optContactPerson, "Country", country,
295 "Organization", organization, "OrganizationalUnit",
296 organizationalUnit, "State", state, "CertificateCollection",
297 certificateCollection, "AlternativeNames", optAlternativeNames,
298 "ChallengePassword", optChallengePassword, "Email", optEmail,
299 "GivenName", optGivenName, "Initials", optInitials,
300 "KeyBitLength", optKeyBitLength, "KeyCurveId", optKeyCurveId,
301 "KeyPairAlgorithm", optKeyPairAlgorithm, "KeyUsage",
302 optKeyUsage, "Surname", optSurname, "UnstructuredName",
303 optUnstructuredName))
304 {
305 return;
306 }
307
308 // bmcweb has no way to store or decode a private key challenge
309 // password, which will likely cause bmcweb to crash on startup if this
310 // is not set on a post so not allowing the user to set value
311 if (*optChallengePassword != "")
312 {
313 messages::actionParameterNotSupported(asyncResp->res, "GenerateCSR",
314 "ChallengePassword");
315 return;
316 }
317
318 std::string certURI;
319 if (!redfish::json_util::readJson(certificateCollection, asyncResp->res,
320 "@odata.id", certURI))
321 {
322 return;
323 }
324
325 std::string objectPath;
326 std::string service;
327 if (boost::starts_with(
328 certURI,
329 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
330 {
331 objectPath = certs::httpsObjectPath;
332 service = certs::httpsServiceName;
333 }
Marri Devender Rao3b7f0142019-05-17 02:53:23 -0500334 else if (boost::starts_with(
335 certURI, "/redfish/v1/AccountService/LDAP/Certificates"))
336 {
337 objectPath = certs::ldapObjectPath;
338 service = certs::ldapServiceName;
339 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500340 else
341 {
342 messages::actionParameterNotSupported(
343 asyncResp->res, "CertificateCollection", "GenerateCSR");
344 return;
345 }
346
347 // supporting only EC and RSA algorithm
348 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA")
349 {
350 messages::actionParameterNotSupported(
351 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
352 return;
353 }
354
355 // supporting only 2048 key bit length for RSA algorithm due to time
356 // consumed in generating private key
357 if (*optKeyPairAlgorithm == "RSA" &&
358 *optKeyBitLength != RSA_KEY_BIT_LENGTH)
359 {
360 messages::propertyValueNotInList(asyncResp->res,
361 std::to_string(*optKeyBitLength),
362 "KeyBitLength");
363 return;
364 }
365
366 // validate KeyUsage supporting only 1 type based on URL
367 if (boost::starts_with(
368 certURI,
369 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
370 {
371 if (optKeyUsage->size() == 0)
372 {
373 optKeyUsage->push_back("ServerAuthentication");
374 }
375 else if (optKeyUsage->size() == 1)
376 {
377 if ((*optKeyUsage)[0] != "ServerAuthentication")
378 {
379 messages::propertyValueNotInList(
380 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
381 return;
382 }
383 }
384 else
385 {
386 messages::actionParameterNotSupported(
387 asyncResp->res, "KeyUsage", "GenerateCSR");
388 return;
389 }
390 }
Marri Devender Rao3b7f0142019-05-17 02:53:23 -0500391 else if (boost::starts_with(
392 certURI, "/redfish/v1/AccountService/LDAP/Certificates"))
393 {
394 if (optKeyUsage->size() == 0)
395 {
396 optKeyUsage->push_back("ClientAuthentication");
397 }
398 else if (optKeyUsage->size() == 1)
399 {
400 if ((*optKeyUsage)[0] != "ClientAuthentication")
401 {
402 messages::propertyValueNotInList(
403 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
404 return;
405 }
406 }
407 else
408 {
409 messages::actionParameterNotSupported(
410 asyncResp->res, "KeyUsage", "GenerateCSR");
411 return;
412 }
413 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500414
415 // Only allow one CSR matcher at a time so setting retry time-out and
416 // timer expiry to 10 seconds for now.
417 static const int TIME_OUT = 10;
418 if (csrMatcher)
419 {
Marri Devender Rao30215812019-03-18 08:59:21 -0500420 messages::serviceTemporarilyUnavailable(asyncResp->res,
421 std::to_string(TIME_OUT));
422 return;
423 }
424
425 // Make this static so it survives outside this method
426 static boost::asio::steady_timer timeout(*req.ioService);
427 timeout.expires_after(std::chrono::seconds(TIME_OUT));
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500428 timeout.async_wait([asyncResp](const boost::system::error_code& ec) {
Marri Devender Rao30215812019-03-18 08:59:21 -0500429 csrMatcher = nullptr;
430 if (ec)
431 {
432 // operation_aborted is expected if timer is canceled before
433 // completion.
434 if (ec != boost::asio::error::operation_aborted)
435 {
436 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
437 }
438 return;
439 }
440 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
441 messages::internalError(asyncResp->res);
442 });
443
444 // create a matcher to wait on CSR object
445 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
446 std::string match("type='signal',"
447 "interface='org.freedesktop.DBus.ObjectManager',"
448 "path='" +
449 objectPath +
450 "',"
451 "member='InterfacesAdded'");
452 csrMatcher = std::make_unique<sdbusplus::bus::match::match>(
453 *crow::connections::systemBus, match,
454 [asyncResp, service, objectPath,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500455 certURI](sdbusplus::message::message& m) {
Ed Tanous271584a2019-07-09 16:24:22 -0700456 timeout.cancel();
Marri Devender Rao30215812019-03-18 08:59:21 -0500457 if (m.is_method_error())
458 {
459 BMCWEB_LOG_ERROR << "Dbus method error!!!";
460 messages::internalError(asyncResp->res);
461 return;
462 }
463 std::vector<std::pair<
464 std::string, std::vector<std::pair<
465 std::string, std::variant<std::string>>>>>
466 interfacesProperties;
467 sdbusplus::message::object_path csrObjectPath;
468 m.read(csrObjectPath, interfacesProperties);
469 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500470 for (auto& interface : interfacesProperties)
Marri Devender Rao30215812019-03-18 08:59:21 -0500471 {
472 if (interface.first == "xyz.openbmc_project.Certs.CSR")
473 {
474 getCSR(asyncResp, certURI, service, objectPath,
475 csrObjectPath.str);
476 break;
477 }
478 }
479 });
480 crow::connections::systemBus->async_method_call(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500481 [asyncResp](const boost::system::error_code& ec,
Ed Tanouscb13a392020-07-25 19:02:03 +0000482 const std::string&) {
Marri Devender Rao30215812019-03-18 08:59:21 -0500483 if (ec)
484 {
485 BMCWEB_LOG_ERROR << "DBUS response error: " << ec.message();
486 messages::internalError(asyncResp->res);
487 return;
488 }
489 },
490 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
491 "GenerateCSR", *optAlternativeNames, *optChallengePassword, city,
492 commonName, *optContactPerson, country, *optEmail, *optGivenName,
493 *optInitials, *optKeyBitLength, *optKeyCurveId,
494 *optKeyPairAlgorithm, *optKeyUsage, organization,
495 organizationalUnit, state, *optSurname, *optUnstructuredName);
496 }
497}; // CertificateActionGenerateCSR
498
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600499/**
Gunnar Mills4e0453b2020-07-08 14:00:30 -0500500 * @brief Parse and update Certificate Issue/Subject property
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600501 *
502 * @param[in] asyncResp Shared pointer to the response message
503 * @param[in] str Issuer/Subject value in key=value pairs
504 * @param[in] type Issuer/Subject
505 * @return None
506 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500507static void updateCertIssuerOrSubject(nlohmann::json& out,
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600508 const std::string_view value)
509{
510 // example: O=openbmc-project.xyz,CN=localhost
511 std::string_view::iterator i = value.begin();
512 while (i != value.end())
513 {
514 std::string_view::iterator tokenBegin = i;
515 while (i != value.end() && *i != '=')
516 {
517 i++;
518 }
519 if (i == value.end())
520 {
521 break;
522 }
Ed Tanous271584a2019-07-09 16:24:22 -0700523 const std::string_view key(tokenBegin,
524 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600525 i++;
526 tokenBegin = i;
527 while (i != value.end() && *i != ',')
528 {
529 i++;
530 }
Ed Tanous271584a2019-07-09 16:24:22 -0700531 const std::string_view val(tokenBegin,
532 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600533 if (key == "L")
534 {
535 out["City"] = val;
536 }
537 else if (key == "CN")
538 {
539 out["CommonName"] = val;
540 }
541 else if (key == "C")
542 {
543 out["Country"] = val;
544 }
545 else if (key == "O")
546 {
547 out["Organization"] = val;
548 }
549 else if (key == "OU")
550 {
551 out["OrganizationalUnit"] = val;
552 }
553 else if (key == "ST")
554 {
555 out["State"] = val;
556 }
557 // skip comma character
558 if (i != value.end())
559 {
560 i++;
561 }
562 }
563}
564
565/**
566 * @brief Retrieve the certificates properties and append to the response
567 * message
568 *
569 * @param[in] asyncResp Shared pointer to the response message
570 * @param[in] objectPath Path of the D-Bus service object
571 * @param[in] certId Id of the certificate
572 * @param[in] certURL URL of the certificate object
573 * @param[in] name name of the certificate
574 * @return None
575 */
576static void getCertificateProperties(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500577 const std::shared_ptr<AsyncResp>& asyncResp, const std::string& objectPath,
578 const std::string& service, long certId, const std::string& certURL,
579 const std::string& name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600580{
581 using PropertyType =
582 std::variant<std::string, uint64_t, std::vector<std::string>>;
583 using PropertiesMap = boost::container::flat_map<std::string, PropertyType>;
584 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
585 << " certId=" << certId << " certURl=" << certURL;
586 crow::connections::systemBus->async_method_call(
Marri Devender Rao37cce912019-02-20 01:05:22 -0600587 [asyncResp, certURL, certId, name](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500588 const PropertiesMap& properties) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600589 if (ec)
590 {
591 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500592 messages::resourceNotFound(asyncResp->res, name,
593 std::to_string(certId));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600594 return;
595 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600596 asyncResp->res.jsonValue = {
597 {"@odata.id", certURL},
598 {"@odata.type", "#Certificate.v1_0_0.Certificate"},
Marri Devender Rao37cce912019-02-20 01:05:22 -0600599 {"Id", std::to_string(certId)},
600 {"Name", name},
601 {"Description", name}};
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500602 for (const auto& property : properties)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600603 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600604 if (property.first == "CertificateString")
605 {
606 asyncResp->res.jsonValue["CertificateString"] = "";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500607 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600608 std::get_if<std::string>(&property.second);
609 if (value)
610 {
611 asyncResp->res.jsonValue["CertificateString"] = *value;
612 }
613 }
614 else if (property.first == "KeyUsage")
615 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500616 nlohmann::json& keyUsage =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600617 asyncResp->res.jsonValue["KeyUsage"];
618 keyUsage = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500619 const std::vector<std::string>* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600620 std::get_if<std::vector<std::string>>(&property.second);
621 if (value)
622 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500623 for (const std::string& usage : *value)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600624 {
625 keyUsage.push_back(usage);
626 }
627 }
628 }
629 else if (property.first == "Issuer")
630 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500631 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600632 std::get_if<std::string>(&property.second);
633 if (value)
634 {
635 updateCertIssuerOrSubject(
636 asyncResp->res.jsonValue["Issuer"], *value);
637 }
638 }
639 else if (property.first == "Subject")
640 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500641 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600642 std::get_if<std::string>(&property.second);
643 if (value)
644 {
645 updateCertIssuerOrSubject(
646 asyncResp->res.jsonValue["Subject"], *value);
647 }
648 }
649 else if (property.first == "ValidNotAfter")
650 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500651 const uint64_t* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600652 std::get_if<uint64_t>(&property.second);
653 if (value)
654 {
655 std::time_t time = static_cast<std::time_t>(*value);
656 asyncResp->res.jsonValue["ValidNotAfter"] =
657 crow::utility::getDateTime(time);
658 }
659 }
660 else if (property.first == "ValidNotBefore")
661 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500662 const uint64_t* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600663 std::get_if<uint64_t>(&property.second);
664 if (value)
665 {
666 std::time_t time = static_cast<std::time_t>(*value);
667 asyncResp->res.jsonValue["ValidNotBefore"] =
668 crow::utility::getDateTime(time);
669 }
670 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600671 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600672 asyncResp->res.addHeader("Location", certURL);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600673 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600674 service, objectPath, certs::dbusPropIntf, "GetAll",
675 certs::certPropIntf);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600676}
677
678using GetObjectType =
679 std::vector<std::pair<std::string, std::vector<std::string>>>;
680
681/**
682 * Action to replace an existing certificate
683 */
684class CertificateActionsReplaceCertificate : public Node
685{
686 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700687 CertificateActionsReplaceCertificate(App& app) :
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600688 Node(app, "/redfish/v1/CertificateService/Actions/"
689 "CertificateService.ReplaceCertificate/")
690 {
691 entityPrivileges = {
692 {boost::beast::http::verb::get, {{"Login"}}},
693 {boost::beast::http::verb::head, {{"Login"}}},
694 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
695 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
696 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
697 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
698 }
699
700 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500701 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +0000702 const std::vector<std::string>&) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600703 {
704 std::string certificate;
705 nlohmann::json certificateUri;
706 std::optional<std::string> certificateType = "PEM";
707 auto asyncResp = std::make_shared<AsyncResp>(res);
708 if (!json_util::readJson(req, asyncResp->res, "CertificateString",
709 certificate, "CertificateUri", certificateUri,
710 "CertificateType", certificateType))
711 {
712 BMCWEB_LOG_ERROR << "Required parameters are missing";
713 messages::internalError(asyncResp->res);
714 return;
715 }
716
717 if (!certificateType)
718 {
719 // should never happen, but it never hurts to be paranoid.
720 return;
721 }
722 if (certificateType != "PEM")
723 {
724 messages::actionParameterNotSupported(
725 asyncResp->res, "CertificateType", "ReplaceCertificate");
726 return;
727 }
728
729 std::string certURI;
730 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
731 "@odata.id", certURI))
732 {
733 messages::actionParameterMissing(
734 asyncResp->res, "ReplaceCertificate", "CertificateUri");
735 return;
736 }
737
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600738 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
739 long id = getIDFromURL(certURI);
740 if (id < 0)
741 {
742 messages::actionParameterValueFormatError(asyncResp->res, certURI,
743 "CertificateUri",
744 "ReplaceCertificate");
745 return;
746 }
747 std::string objectPath;
748 std::string name;
Marri Devender Rao37cce912019-02-20 01:05:22 -0600749 std::string service;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600750 if (boost::starts_with(
751 certURI,
752 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"))
753 {
754 objectPath =
755 std::string(certs::httpsObjectPath) + "/" + std::to_string(id);
756 name = "HTTPS certificate";
Marri Devender Rao37cce912019-02-20 01:05:22 -0600757 service = certs::httpsServiceName;
758 }
759 else if (boost::starts_with(
760 certURI, "/redfish/v1/AccountService/LDAP/Certificates/"))
761 {
762 objectPath =
763 std::string(certs::ldapObjectPath) + "/" + std::to_string(id);
764 name = "LDAP certificate";
765 service = certs::ldapServiceName;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600766 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -0500767 else if (boost::starts_with(
768 certURI,
769 "/redfish/v1/Managers/bmc/Truststore/Certificates/"))
770 {
771 objectPath = std::string(certs::authorityObjectPath) + "/" +
772 std::to_string(id);
773 name = "TrustStore certificate";
774 service = certs::authorityServiceName;
775 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600776 else
777 {
778 messages::actionParameterNotSupported(
779 asyncResp->res, "CertificateUri", "ReplaceCertificate");
780 return;
781 }
782
783 std::shared_ptr<CertificateFile> certFile =
784 std::make_shared<CertificateFile>(certificate);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600785 crow::connections::systemBus->async_method_call(
Marri Devender Rao37cce912019-02-20 01:05:22 -0600786 [asyncResp, certFile, objectPath, service, certURI, id,
787 name](const boost::system::error_code ec) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600788 if (ec)
789 {
790 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500791 messages::resourceNotFound(asyncResp->res, name,
792 std::to_string(id));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600793 return;
794 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600795 getCertificateProperties(asyncResp, objectPath, service, id,
796 certURI, name);
797 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
798 << certFile->getCertFilePath();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600799 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600800 service, objectPath, certs::certReplaceIntf, "Replace",
801 certFile->getCertFilePath());
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600802 }
803}; // CertificateActionsReplaceCertificate
804
805/**
806 * Certificate resource describes a certificate used to prove the identity
807 * of a component, account or service.
808 */
809class HTTPSCertificate : public Node
810{
811 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700812 HTTPSCertificate(App& app) :
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600813 Node(app,
814 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"
815 "<str>/",
816 std::string())
817 {
818 entityPrivileges = {
819 {boost::beast::http::verb::get, {{"Login"}}},
820 {boost::beast::http::verb::head, {{"Login"}}},
821 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
822 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
823 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
824 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
825 }
826
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500827 void doGet(crow::Response& res, const crow::Request& req,
828 const std::vector<std::string>& params) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600829 {
830 auto asyncResp = std::make_shared<AsyncResp>(res);
831 if (params.size() != 1)
832 {
833 messages::internalError(asyncResp->res);
834 return;
835 }
836 long id = getIDFromURL(req.url);
837
838 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID=" << std::to_string(id);
839 std::string certURL =
840 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
841 std::to_string(id);
842 std::string objectPath = certs::httpsObjectPath;
843 objectPath += "/";
844 objectPath += std::to_string(id);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600845 getCertificateProperties(asyncResp, objectPath, certs::httpsServiceName,
846 id, certURL, "HTTPS Certificate");
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600847 }
848
849}; // namespace redfish
850
851/**
852 * Collection of HTTPS certificates
853 */
854class HTTPSCertificateCollection : public Node
855{
856 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700857 HTTPSCertificateCollection(App& app) :
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600858 Node(app,
859 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
860 {
861 entityPrivileges = {
862 {boost::beast::http::verb::get, {{"Login"}}},
863 {boost::beast::http::verb::head, {{"Login"}}},
864 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
865 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
866 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
867 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
868 }
Ed Tanouscb13a392020-07-25 19:02:03 +0000869 void doGet(crow::Response& res, const crow::Request&,
870 const std::vector<std::string>&) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600871 {
872 res.jsonValue = {
873 {"@odata.id",
874 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"},
875 {"@odata.type", "#CertificateCollection.CertificateCollection"},
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600876 {"Name", "HTTPS Certificates Collection"},
877 {"Description", "A Collection of HTTPS certificate instances"}};
878 auto asyncResp = std::make_shared<AsyncResp>(res);
879 crow::connections::systemBus->async_method_call(
880 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500881 const ManagedObjectType& certs) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600882 if (ec)
883 {
884 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
885 messages::internalError(asyncResp->res);
886 return;
887 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500888 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
Marri Devender Rao37cce912019-02-20 01:05:22 -0600889 members = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500890 for (const auto& cert : certs)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600891 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600892 long id = getIDFromURL(cert.first.str);
893 if (id >= 0)
894 {
895 members.push_back(
896 {{"@odata.id",
897 "/redfish/v1/Managers/bmc/"
898 "NetworkProtocol/HTTPS/Certificates/" +
899 std::to_string(id)}});
900 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600901 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600902 asyncResp->res.jsonValue["Members@odata.count"] =
903 members.size();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600904 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600905 certs::httpsServiceName, certs::httpsObjectPath,
906 certs::dbusObjManagerIntf, "GetManagedObjects");
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600907 }
908
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500909 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +0000910 const std::vector<std::string>&) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600911 {
912 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
913 auto asyncResp = std::make_shared<AsyncResp>(res);
914 asyncResp->res.jsonValue = {{"Name", "HTTPS Certificate"},
915 {"Description", "HTTPS Certificate"}};
916
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200917 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
918
919 if (certFileBody.empty())
920 {
Zbigniew Kurzynskia08752f2019-10-10 18:27:18 +0200921 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
922 messages::unrecognizedRequestBody(asyncResp->res);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200923 return;
924 }
925
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600926 std::shared_ptr<CertificateFile> certFile =
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200927 std::make_shared<CertificateFile>(certFileBody);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600928
929 crow::connections::systemBus->async_method_call(
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200930 [asyncResp, certFile](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500931 const std::string& objectPath) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600932 if (ec)
933 {
934 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
935 messages::internalError(asyncResp->res);
936 return;
937 }
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200938 long certId = getIDFromURL(objectPath);
939 if (certId < 0)
940 {
941 BMCWEB_LOG_ERROR << "Invalid objectPath value"
942 << objectPath;
943 messages::internalError(asyncResp->res);
944 return;
945 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600946 std::string certURL =
947 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/"
948 "Certificates/" +
949 std::to_string(certId);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600950 getCertificateProperties(asyncResp, objectPath,
951 certs::httpsServiceName, certId,
952 certURL, "HTTPS Certificate");
953 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
954 << certFile->getCertFilePath();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600955 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600956 certs::httpsServiceName, certs::httpsObjectPath,
957 certs::certInstallIntf, "Install", certFile->getCertFilePath());
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600958 }
959}; // HTTPSCertificateCollection
960
961/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600962 * The certificate location schema defines a resource that an administrator
963 * can use in order to locate all certificates installed on a given service.
964 */
965class CertificateLocations : public Node
966{
967 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700968 CertificateLocations(App& app) :
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600969 Node(app, "/redfish/v1/CertificateService/CertificateLocations/")
970 {
971 entityPrivileges = {
972 {boost::beast::http::verb::get, {{"Login"}}},
973 {boost::beast::http::verb::head, {{"Login"}}},
974 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
975 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
976 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
977 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
978 }
979
980 private:
Ed Tanouscb13a392020-07-25 19:02:03 +0000981 void doGet(crow::Response& res, const crow::Request&,
982 const std::vector<std::string>&) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600983 {
984 res.jsonValue = {
985 {"@odata.id",
986 "/redfish/v1/CertificateService/CertificateLocations"},
987 {"@odata.type",
988 "#CertificateLocations.v1_0_0.CertificateLocations"},
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600989 {"Name", "Certificate Locations"},
990 {"Id", "CertificateLocations"},
991 {"Description",
992 "Defines a resource that an administrator can use in order to "
993 "locate all certificates installed on a given service"}};
994 auto asyncResp = std::make_shared<AsyncResp>(res);
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500995 nlohmann::json& links =
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600996 asyncResp->res.jsonValue["Links"]["Certificates"];
997 links = nlohmann::json::array();
998 getCertificateLocations(
999 asyncResp,
1000 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/",
Marri Devender Rao37cce912019-02-20 01:05:22 -06001001 certs::httpsObjectPath, certs::httpsServiceName);
1002 getCertificateLocations(asyncResp,
1003 "/redfish/v1/AccountService/LDAP/Certificates/",
1004 certs::ldapObjectPath, certs::ldapServiceName);
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001005 getCertificateLocations(
1006 asyncResp, "/redfish/v1/Managers/bmc/Truststore/Certificates/",
1007 certs::authorityObjectPath, certs::authorityServiceName);
Marri Devender Rao37cce912019-02-20 01:05:22 -06001008 }
1009 /**
1010 * @brief Retrieve the certificates installed list and append to the
1011 * response
1012 *
1013 * @param[in] asyncResp Shared pointer to the response message
1014 * @param[in] certURL Path of the certificate object
1015 * @param[in] path Path of the D-Bus service object
1016 * @return None
1017 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001018 void getCertificateLocations(std::shared_ptr<AsyncResp>& asyncResp,
1019 const std::string& certURL,
1020 const std::string& path,
1021 const std::string& service)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001022 {
1023 BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL
1024 << " Path=" << path << " service= " << service;
1025 crow::connections::systemBus->async_method_call(
1026 [asyncResp, certURL](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001027 const ManagedObjectType& certs) {
Marri Devender Rao37cce912019-02-20 01:05:22 -06001028 if (ec)
1029 {
1030 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1031 messages::internalError(asyncResp->res);
1032 return;
1033 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001034 nlohmann::json& links =
Marri Devender Rao37cce912019-02-20 01:05:22 -06001035 asyncResp->res.jsonValue["Links"]["Certificates"];
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001036 for (auto& cert : certs)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001037 {
1038 long id = getIDFromURL(cert.first.str);
1039 if (id >= 0)
1040 {
1041 links.push_back(
1042 {{"@odata.id", certURL + std::to_string(id)}});
1043 }
1044 }
1045 asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] =
1046 links.size();
1047 },
1048 service, path, certs::dbusObjManagerIntf, "GetManagedObjects");
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001049 }
1050}; // CertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -06001051
1052/**
1053 * Collection of LDAP certificates
1054 */
1055class LDAPCertificateCollection : public Node
1056{
1057 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001058 LDAPCertificateCollection(App& app) :
Marri Devender Rao37cce912019-02-20 01:05:22 -06001059 Node(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1060 {
1061 entityPrivileges = {
1062 {boost::beast::http::verb::get, {{"Login"}}},
1063 {boost::beast::http::verb::head, {{"Login"}}},
1064 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1065 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1066 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1067 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1068 }
Ed Tanouscb13a392020-07-25 19:02:03 +00001069 void doGet(crow::Response& res, const crow::Request&,
1070 const std::vector<std::string>&) override
Marri Devender Rao37cce912019-02-20 01:05:22 -06001071 {
1072 res.jsonValue = {
1073 {"@odata.id", "/redfish/v1/AccountService/LDAP/Certificates"},
1074 {"@odata.type", "#CertificateCollection.CertificateCollection"},
Marri Devender Rao37cce912019-02-20 01:05:22 -06001075 {"Name", "LDAP Certificates Collection"},
1076 {"Description", "A Collection of LDAP certificate instances"}};
1077 auto asyncResp = std::make_shared<AsyncResp>(res);
1078 crow::connections::systemBus->async_method_call(
1079 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001080 const ManagedObjectType& certs) {
Marri Devender Rao37cce912019-02-20 01:05:22 -06001081 if (ec)
1082 {
1083 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1084 messages::internalError(asyncResp->res);
1085 return;
1086 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001087 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
Marri Devender Rao37cce912019-02-20 01:05:22 -06001088 members = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001089 for (const auto& cert : certs)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001090 {
1091 long id = getIDFromURL(cert.first.str);
1092 if (id >= 0)
1093 {
1094 members.push_back(
1095 {{"@odata.id", "/redfish/v1/AccountService/"
1096 "LDAP/Certificates/" +
1097 std::to_string(id)}});
1098 }
1099 }
1100 asyncResp->res.jsonValue["Members@odata.count"] =
1101 members.size();
1102 },
1103 certs::ldapServiceName, certs::ldapObjectPath,
1104 certs::dbusObjManagerIntf, "GetManagedObjects");
1105 }
1106
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001107 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00001108 const std::vector<std::string>&) override
Marri Devender Rao37cce912019-02-20 01:05:22 -06001109 {
Marri Devender Rao37cce912019-02-20 01:05:22 -06001110 auto asyncResp = std::make_shared<AsyncResp>(res);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +02001111 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1112
1113 if (certFileBody.empty())
1114 {
Zbigniew Kurzynskia08752f2019-10-10 18:27:18 +02001115 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1116 messages::unrecognizedRequestBody(asyncResp->res);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +02001117 return;
1118 }
1119
1120 std::shared_ptr<CertificateFile> certFile =
1121 std::make_shared<CertificateFile>(certFileBody);
1122
Marri Devender Rao37cce912019-02-20 01:05:22 -06001123 crow::connections::systemBus->async_method_call(
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001124 [asyncResp, certFile](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001125 const std::string& objectPath) {
Marri Devender Rao37cce912019-02-20 01:05:22 -06001126 if (ec)
1127 {
1128 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1129 messages::internalError(asyncResp->res);
1130 return;
1131 }
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001132 long certId = getIDFromURL(objectPath);
1133 if (certId < 0)
1134 {
1135 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1136 << objectPath;
1137 messages::internalError(asyncResp->res);
1138 return;
1139 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001140 std::string certURL =
1141 "/redfish/v1/AccountService/LDAP/Certificates/" +
1142 std::to_string(certId);
Marri Devender Rao37cce912019-02-20 01:05:22 -06001143 getCertificateProperties(asyncResp, objectPath,
1144 certs::ldapServiceName, certId,
1145 certURL, "LDAP Certificate");
1146 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1147 << certFile->getCertFilePath();
1148 },
1149 certs::ldapServiceName, certs::ldapObjectPath,
1150 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1151 }
1152}; // LDAPCertificateCollection
1153
1154/**
1155 * Certificate resource describes a certificate used to prove the identity
1156 * of a component, account or service.
1157 */
1158class LDAPCertificate : public Node
1159{
1160 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001161 LDAPCertificate(App& app) :
Marri Devender Rao37cce912019-02-20 01:05:22 -06001162 Node(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/",
1163 std::string())
1164 {
1165 entityPrivileges = {
1166 {boost::beast::http::verb::get, {{"Login"}}},
1167 {boost::beast::http::verb::head, {{"Login"}}},
1168 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1169 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1170 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1171 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1172 }
1173
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001174 void doGet(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00001175 const std::vector<std::string>&) override
Marri Devender Rao37cce912019-02-20 01:05:22 -06001176 {
1177 auto asyncResp = std::make_shared<AsyncResp>(res);
1178 long id = getIDFromURL(req.url);
1179 if (id < 0)
1180 {
1181 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1182 messages::internalError(asyncResp->res);
1183 return;
1184 }
1185 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << std::to_string(id);
1186 std::string certURL = "/redfish/v1/AccountService/LDAP/Certificates/" +
1187 std::to_string(id);
1188 std::string objectPath = certs::ldapObjectPath;
1189 objectPath += "/";
1190 objectPath += std::to_string(id);
1191 getCertificateProperties(asyncResp, objectPath, certs::ldapServiceName,
1192 id, certURL, "LDAP Certificate");
1193 }
1194}; // LDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001195/**
1196 * Collection of TrustStoreCertificate certificates
1197 */
1198class TrustStoreCertificateCollection : public Node
1199{
1200 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001201 TrustStoreCertificateCollection(App& app) :
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001202 Node(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
1203 {
1204 entityPrivileges = {
1205 {boost::beast::http::verb::get, {{"Login"}}},
1206 {boost::beast::http::verb::head, {{"Login"}}},
1207 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1208 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1209 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1210 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1211 }
Ed Tanouscb13a392020-07-25 19:02:03 +00001212 void doGet(crow::Response& res, const crow::Request&,
1213 const std::vector<std::string>&) override
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001214 {
1215 res.jsonValue = {
1216 {"@odata.id", "/redfish/v1/Managers/bmc/Truststore/Certificates/"},
1217 {"@odata.type", "#CertificateCollection.CertificateCollection"},
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001218 {"Name", "TrustStore Certificates Collection"},
1219 {"Description",
1220 "A Collection of TrustStore certificate instances"}};
1221 auto asyncResp = std::make_shared<AsyncResp>(res);
1222 crow::connections::systemBus->async_method_call(
1223 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001224 const ManagedObjectType& certs) {
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001225 if (ec)
1226 {
1227 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1228 messages::internalError(asyncResp->res);
1229 return;
1230 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001231 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001232 members = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001233 for (const auto& cert : certs)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001234 {
1235 long id = getIDFromURL(cert.first.str);
1236 if (id >= 0)
1237 {
1238 members.push_back(
1239 {{"@odata.id", "/redfish/v1/Managers/bmc/"
1240 "Truststore/Certificates/" +
1241 std::to_string(id)}});
1242 }
1243 }
1244 asyncResp->res.jsonValue["Members@odata.count"] =
1245 members.size();
1246 },
1247 certs::authorityServiceName, certs::authorityObjectPath,
1248 certs::dbusObjManagerIntf, "GetManagedObjects");
1249 }
1250
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001251 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00001252 const std::vector<std::string>&) override
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001253 {
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001254 auto asyncResp = std::make_shared<AsyncResp>(res);
Zbigniew Kurzynskia08752f2019-10-10 18:27:18 +02001255 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1256
1257 if (certFileBody.empty())
1258 {
1259 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1260 messages::unrecognizedRequestBody(asyncResp->res);
1261 return;
1262 }
1263
1264 std::shared_ptr<CertificateFile> certFile =
1265 std::make_shared<CertificateFile>(certFileBody);
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001266 crow::connections::systemBus->async_method_call(
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001267 [asyncResp, certFile](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001268 const std::string& objectPath) {
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001269 if (ec)
1270 {
1271 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1272 messages::internalError(asyncResp->res);
1273 return;
1274 }
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001275 long certId = getIDFromURL(objectPath);
1276 if (certId < 0)
1277 {
1278 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1279 << objectPath;
1280 messages::internalError(asyncResp->res);
1281 return;
1282 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001283 std::string certURL = "/redfish/v1/Managers/bmc/"
1284 "Truststore/Certificates/" +
1285 std::to_string(certId);
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001286
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001287 getCertificateProperties(asyncResp, objectPath,
1288 certs::authorityServiceName, certId,
1289 certURL, "TrustStore Certificate");
1290 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1291 << certFile->getCertFilePath();
1292 },
1293 certs::authorityServiceName, certs::authorityObjectPath,
1294 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1295 }
1296}; // TrustStoreCertificateCollection
1297
1298/**
1299 * Certificate resource describes a certificate used to prove the identity
1300 * of a component, account or service.
1301 */
1302class TrustStoreCertificate : public Node
1303{
1304 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001305 TrustStoreCertificate(App& app) :
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001306 Node(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/",
1307 std::string())
1308 {
1309 entityPrivileges = {
1310 {boost::beast::http::verb::get, {{"Login"}}},
1311 {boost::beast::http::verb::head, {{"Login"}}},
1312 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1313 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1314 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1315 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1316 }
1317
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001318 void doGet(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00001319 const std::vector<std::string>&) override
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001320 {
1321 auto asyncResp = std::make_shared<AsyncResp>(res);
1322 long id = getIDFromURL(req.url);
1323 if (id < 0)
1324 {
1325 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1326 messages::internalError(asyncResp->res);
1327 return;
1328 }
1329 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID="
1330 << std::to_string(id);
1331 std::string certURL =
1332 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1333 std::to_string(id);
1334 std::string objectPath = certs::authorityObjectPath;
1335 objectPath += "/";
1336 objectPath += std::to_string(id);
1337 getCertificateProperties(asyncResp, objectPath,
1338 certs::authorityServiceName, id, certURL,
1339 "TrustStore Certificate");
1340 }
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001341
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001342 void doDelete(crow::Response& res, const crow::Request& req,
1343 const std::vector<std::string>& params) override
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001344 {
1345 auto asyncResp = std::make_shared<AsyncResp>(res);
1346
1347 if (params.size() != 1)
1348 {
1349 messages::internalError(asyncResp->res);
1350 return;
1351 }
1352
1353 long id = getIDFromURL(req.url);
1354 if (id < 0)
1355 {
1356 BMCWEB_LOG_ERROR << "Invalid url value: " << req.url;
1357 messages::resourceNotFound(asyncResp->res, "TrustStore Certificate",
1358 std::string(req.url));
1359 return;
1360 }
1361 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doDelete ID="
1362 << std::to_string(id);
1363 std::string certPath = certs::authorityObjectPath;
1364 certPath += "/";
1365 certPath += std::to_string(id);
1366
1367 crow::connections::systemBus->async_method_call(
1368 [asyncResp, id](const boost::system::error_code ec) {
1369 if (ec)
1370 {
1371 messages::resourceNotFound(asyncResp->res,
1372 "TrustStore Certificate",
1373 std::to_string(id));
1374 return;
1375 }
1376 BMCWEB_LOG_INFO << "Certificate deleted";
1377 asyncResp->res.result(boost::beast::http::status::no_content);
1378 },
1379 certs::authorityServiceName, certPath, certs::objDeleteIntf,
1380 "Delete");
1381 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001382}; // TrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001383} // namespace redfish