summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEd Tanous <ed@tanous.net>2024-04-13 21:51:10 +0300
committerEd Tanous <ed@tanous.net>2024-05-03 03:48:07 +0300
commit1aa375b80075c7e1acdc9188440a62bab21b8651 (patch)
tree661662d0e68d52a8770fd3b0f5c36f472fb8a9d9
parent578556628ee1200d8b77d7caca3a27e427847f52 (diff)
downloadbmcweb-1aa375b80075c7e1acdc9188440a62bab21b8651.tar.xz
Implement client certificate schemas
The Redfish standard seems to have caught up with some of the OEM schemas and features we already have, namely MutualTLS and Basic Auth disablement. This commit implements most of the GET parameters for which we already have backends. ClientCertificate is pointed to the same resources as TrustStore. Tested: generate_auth_certificates.py succeeds, and shows a certificate in ClientCertificate collection Get AccountService, and ClientAuthentication/Certificates returns expected values. Redfish service validator passes. Change-Id: If18e34e9dfa8f38293fceff288596811afd16d4a Signed-off-by: Ed Tanous <ed@tanous.net>
-rw-r--r--Redfish.md29
-rw-r--r--redfish-core/include/utils/collection.hpp6
-rw-r--r--redfish-core/lib/account_service.hpp126
3 files changed, 160 insertions, 1 deletions
diff --git a/Redfish.md b/Redfish.md
index 5e410ad616..cfee9d5110 100644
--- a/Redfish.md
+++ b/Redfish.md
@@ -62,6 +62,10 @@ Fields common to all schemas
- LDAP
- MaxPasswordLength
- MinPasswordLength
+- MultiFactorAuth/ClientCertificate/Certificates
+- MultiFactorAuth/ClientCertificate/CertificateMappingAttribute
+- MultiFactorAuth/ClientCertificate/Enabled
+- MultiFactorAuth/ClientCertificate/RespondToUnauthenticatedClients
- Oem/OpenBMC/AuthMethods/BasicAuth
- Oem/OpenBMC/AuthMethods/Cookie
- Oem/OpenBMC/AuthMethods/SessionToken
@@ -70,6 +74,31 @@ Fields common to all schemas
- Roles
- ServiceEnabled
+### /redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates
+
+- Members
+- Members@odata.count
+
+### /redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/{Certificate}
+
+- CertificateString
+- Id
+- Issuer/City
+- Issuer/CommonName
+- Issuer/Country
+- Issuer/Organization
+- Issuer/OrganizationalUnit
+- Issuer/State
+- KeyUsage
+- Subject/City
+- Subject/Country
+- Subject/CommonName
+- Subject/Organization
+- Subject/OrganizationalUnit
+- Subject/State
+- ValidNotAfter
+- ValidNotBefore
+
### /redfish/v1/AggregationService/
#### AggregationService
diff --git a/redfish-core/include/utils/collection.hpp b/redfish-core/include/utils/collection.hpp
index 2e5563b4c9..9efa7d3ef3 100644
--- a/redfish-core/include/utils/collection.hpp
+++ b/redfish-core/include/utils/collection.hpp
@@ -27,6 +27,12 @@ inline void handleCollectionMembers(
const boost::system::error_code& ec,
const dbus::utility::MapperGetSubTreePathsResponse& objects)
{
+ if (jsonKeyName.empty())
+ {
+ messages::internalError(asyncResp->res);
+ BMCWEB_LOG_ERROR("Json Key called empty. Did you mean /Members?");
+ return;
+ }
nlohmann::json::json_pointer jsonCountKeyName = jsonKeyName;
std::string back = jsonCountKeyName.back();
jsonCountKeyName.pop_back();
diff --git a/redfish-core/lib/account_service.hpp b/redfish-core/lib/account_service.hpp
index 972512b145..1d3ef411a8 100644
--- a/redfish-core/lib/account_service.hpp
+++ b/redfish-core/lib/account_service.hpp
@@ -16,6 +16,7 @@
#pragma once
#include "app.hpp"
+#include "certificate_service.hpp"
#include "dbus_utility.hpp"
#include "error_messages.hpp"
#include "generated/enums/account_service.hpp"
@@ -23,13 +24,17 @@
#include "persistent_data.hpp"
#include "query.hpp"
#include "registries/privilege_registry.hpp"
+#include "utils/collection.hpp"
#include "utils/dbus_utils.hpp"
#include "utils/json_utils.hpp"
+#include <boost/url/format.hpp>
+#include <boost/url/url.hpp>
#include <sdbusplus/asio/property.hpp>
#include <sdbusplus/unpack_properties.hpp>
#include <array>
+#include <memory>
#include <optional>
#include <ranges>
#include <string>
@@ -1174,6 +1179,80 @@ inline void handleAccountServiceHead(
}
inline void
+ getClientCertificates(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const nlohmann::json::json_pointer& keyLocation)
+{
+ boost::urls::url url(
+ "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates");
+ std::array<std::string_view, 1> interfaces = {
+ "xyz.openbmc_project.Certs.Certificate"};
+ std::string path = "/xyz/openbmc_project/certs/authority/truststore";
+
+ collection_util::getCollectionToKey(asyncResp, url, interfaces, path,
+ keyLocation);
+}
+
+inline void handleAccountServiceClientCertificatesInstanceHead(
+ App& app, const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& /*id*/)
+{
+ if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+ {
+ return;
+ }
+
+ asyncResp->res.addHeader(
+ boost::beast::http::field::link,
+ "</redfish/v1/JsonSchemas/Certificate/Certificate.json>; rel=describedby");
+}
+
+inline void handleAccountServiceClientCertificatesInstanceGet(
+ App& app, const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
+{
+ if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+ {
+ return;
+ }
+ BMCWEB_LOG_DEBUG("ClientCertificate Certificate ID={}", id);
+ const boost::urls::url certURL = boost::urls::format(
+ "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/{}",
+ id);
+ std::string objPath =
+ sdbusplus::message::object_path(certs::authorityObjectPath) / id;
+ getCertificateProperties(
+ asyncResp, objPath,
+ "xyz.openbmc_project.Certs.Manager.Authority.Truststore", id, certURL,
+ "Client Certificate");
+}
+
+inline void handleAccountServiceClientCertificatesHead(
+ App& app, const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+{
+ if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+ {
+ return;
+ }
+
+ asyncResp->res.addHeader(
+ boost::beast::http::field::link,
+ "</redfish/v1/JsonSchemas/CertificateCollection/CertificateCollection.json>; rel=describedby");
+}
+
+inline void handleAccountServiceClientCertificatesGet(
+ App& app, const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+{
+ if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+ {
+ return;
+ }
+ getClientCertificates(asyncResp, "/Members"_json_pointer);
+}
+
+inline void
handleAccountServiceGet(App& app, const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
{
@@ -1214,6 +1293,23 @@ inline void
allowed.emplace_back(account_service::BasicAuthState::Disabled);
json["HTTPBasicAuth@AllowableValues"] = std::move(allowed);
+ nlohmann::json::object_t clientCertificate;
+ clientCertificate["Enabled"] = authMethodsConfig.tls;
+ clientCertificate["RespondToUnauthenticatedClients"] = true;
+ clientCertificate["CertificateMappingAttribute"] =
+ account_service::CertificateMappingAttribute::CommonName;
+ nlohmann::json::object_t certificates;
+ certificates["@odata.id"] =
+ "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates";
+ certificates["@odata.type"] =
+ "#CertificateCollection.CertificateCollection";
+ clientCertificate["Certificates"] = std::move(certificates);
+ json["MultiFactorAuth"]["ClientCertificate"] = std::move(clientCertificate);
+
+ getClientCertificates(
+ asyncResp,
+ "/MultiFactorAuth/ClientCertificate/Certificates/Members"_json_pointer);
+
json["Oem"]["OpenBMC"]["@odata.type"] =
"#OpenBMCAccountService.v1_0_0.AccountService";
json["Oem"]["OpenBMC"]["@odata.id"] =
@@ -1249,7 +1345,7 @@ inline void
return;
}
- BMCWEB_LOG_DEBUG("Got {}properties for AccountService",
+ BMCWEB_LOG_DEBUG("Got {} properties for AccountService",
propertiesList.size());
const uint8_t* minPasswordLength = nullptr;
@@ -2026,6 +2122,34 @@ inline void requestAccountServiceRoutes(App& app)
.methods(boost::beast::http::verb::patch)(
std::bind_front(handleAccountServicePatch, std::ref(app)));
+ BMCWEB_ROUTE(
+ app,
+ "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates")
+ .privileges(redfish::privileges::headCertificateCollection)
+ .methods(boost::beast::http::verb::head)(std::bind_front(
+ handleAccountServiceClientCertificatesHead, std::ref(app)));
+
+ BMCWEB_ROUTE(
+ app,
+ "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates")
+ .privileges(redfish::privileges::getCertificateCollection)
+ .methods(boost::beast::http::verb::get)(std::bind_front(
+ handleAccountServiceClientCertificatesGet, std::ref(app)));
+
+ BMCWEB_ROUTE(
+ app,
+ "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/<str>")
+ .privileges(redfish::privileges::headCertificate)
+ .methods(boost::beast::http::verb::head)(std::bind_front(
+ handleAccountServiceClientCertificatesInstanceHead, std::ref(app)));
+
+ BMCWEB_ROUTE(
+ app,
+ "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/<str>/")
+ .privileges(redfish::privileges::getCertificate)
+ .methods(boost::beast::http::verb::get)(std::bind_front(
+ handleAccountServiceClientCertificatesInstanceGet, std::ref(app)));
+
BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
.privileges(redfish::privileges::headManagerAccountCollection)
.methods(boost::beast::http::verb::head)(