summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt15
-rw-r--r--http/http_connection.h156
-rw-r--r--include/ssl_key_handler.hpp7
3 files changed, 176 insertions, 2 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 32c6fad..b93f342 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -77,6 +77,12 @@ option (
'/redfish/v1/Systems/system/'."
OFF
)
+option (
+ BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
+ "Enables authenticating users through TLS client certificates.
+ The BMCWEB_INSECURE_DISABLE_SSL must be OFF for this option to take effect."
+ OFF
+)
# Insecure options. Every option that starts with a BMCWEB_INSECURE flag should
# not be enabled by default for any platform, unless the author fully
@@ -108,6 +114,14 @@ option (
OFF
)
+
+
+if (BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION AND BMCWEB_INSECURE_DISABLE_SSL)
+ message("SSL Must be enabled to allow SSL authentication")
+ set(BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION OFF)
+endif()
+
+
include (CTest)
set (CMAKE_CXX_STANDARD 17)
@@ -344,6 +358,7 @@ install (TARGETS bmcweb DESTINATION bin)
target_compile_definitions (
bmcweb PRIVATE $<$<BOOL:${BMCWEB_ENABLE_KVM}>: -DBMCWEB_ENABLE_KVM>
+ $<$<BOOL:${BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION}>: -DBMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION>
$<$<BOOL:${BMCWEB_ENABLE_VM_WEBSOCKET}>: -DBMCWEB_ENABLE_VM_WEBSOCKET>
$<$<BOOL:${BMCWEB_ENABLE_DBUS_REST}>: -DBMCWEB_ENABLE_DBUS_REST>
$<$<BOOL:${BMCWEB_ENABLE_REDFISH}>: -DBMCWEB_ENABLE_REDFISH>
diff --git a/http/http_connection.h b/http/http_connection.h
index 7cab789..78805a6 100644
--- a/http/http_connection.h
+++ b/http/http_connection.h
@@ -263,6 +263,141 @@ class Connection
// mechanism
parser->body_limit(httpReqBodyLimit);
req.emplace(parser->get());
+
+#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
+ adaptor.set_verify_callback(
+ [this](bool preverified, boost::asio::ssl::verify_context& ctx) {
+ // We always return true to allow full auth flow
+ if (!preverified)
+ {
+ return true;
+ }
+
+ X509_STORE_CTX* cts = ctx.native_handle();
+ if (cts == nullptr)
+ {
+ return true;
+ }
+
+ // Get certificate
+ X509* peerCert =
+ X509_STORE_CTX_get_current_cert(ctx.native_handle());
+ if (peerCert == nullptr)
+ {
+ return true;
+ }
+
+ // Check if certificate is OK
+ int error = X509_STORE_CTX_get_error(cts);
+ if (error != X509_V_OK)
+ {
+ return true;
+ }
+ // Check that we have reached final certificate in chain
+ int32_t depth = X509_STORE_CTX_get_error_depth(cts);
+ if (depth != 0)
+
+ {
+ BMCWEB_LOG_DEBUG
+ << "Certificate verification in progress (depth "
+ << depth << "), waiting to reach final depth";
+ return true;
+ }
+
+ BMCWEB_LOG_DEBUG << "Certificate verification of final depth";
+
+ // Verify KeyUsage
+ bool isKeyUsageDigitalSignature = false;
+ bool isKeyUsageKeyAgreement = false;
+
+ ASN1_BIT_STRING* usage = static_cast<ASN1_BIT_STRING*>(
+ X509_get_ext_d2i(peerCert, NID_key_usage, NULL, NULL));
+
+ if (usage == nullptr)
+ {
+ return true;
+ }
+
+ for (int i = 0; i < usage->length; i++)
+ {
+ if (KU_DIGITAL_SIGNATURE & usage->data[i])
+ {
+ isKeyUsageDigitalSignature = true;
+ }
+ if (KU_KEY_AGREEMENT & usage->data[i])
+ {
+ isKeyUsageKeyAgreement = true;
+ }
+ }
+
+ if (!isKeyUsageDigitalSignature || !isKeyUsageKeyAgreement)
+ {
+ BMCWEB_LOG_DEBUG << "Certificate ExtendedKeyUsage does "
+ "not allow provided certificate to "
+ "be used for user authentication";
+ return true;
+ }
+
+ // Determine that ExtendedKeyUsage includes Client Auth
+
+ stack_st_ASN1_OBJECT* extUsage =
+ static_cast<stack_st_ASN1_OBJECT*>(X509_get_ext_d2i(
+ peerCert, NID_ext_key_usage, NULL, NULL));
+
+ if (extUsage == nullptr)
+ {
+ return true;
+ }
+
+ bool isExKeyUsageClientAuth = false;
+ for (int i = 0; i < sk_ASN1_OBJECT_num(extUsage); i++)
+ {
+ if (NID_client_auth ==
+ OBJ_obj2nid(sk_ASN1_OBJECT_value(extUsage, i)))
+ {
+ isExKeyUsageClientAuth = true;
+ break;
+ }
+ }
+
+ // Certificate has to have proper key usages set
+ if (!isExKeyUsageClientAuth)
+ {
+ BMCWEB_LOG_DEBUG << "Certificate ExtendedKeyUsage does "
+ "not allow provided certificate to "
+ "be used for user authentication";
+ return true;
+ }
+ std::string sslUser;
+ // Extract username contained in CommonName
+ sslUser.resize(256, '\0');
+
+ int status = X509_NAME_get_text_by_NID(
+ X509_get_subject_name(peerCert), NID_commonName,
+ sslUser.data(), static_cast<int>(sslUser.size()));
+
+ if (status == -1)
+ {
+ return true;
+ }
+
+ size_t lastChar = sslUser.find('\0');
+ if (lastChar == std::string::npos || lastChar == 0)
+ {
+ return true;
+ }
+ sslUser.resize(lastChar - 1);
+
+ session =
+ persistent_data::SessionStore::getInstance()
+ .generateUserSession(
+ sslUser,
+ crow::persistent_data::PersistenceType::TIMEOUT);
+
+ return true;
+ });
+#endif // BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
+
#ifdef BMCWEB_ENABLE_DEBUG
connectionCount++;
BMCWEB_LOG_DEBUG << this << " Connection open, total "
@@ -344,6 +479,16 @@ class Connection
req->middlewareContext = static_cast<void*>(&ctx);
req->ioService = static_cast<decltype(req->ioService)>(
&adaptor.get_executor().context());
+
+#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
+ if (auto sp = session.lock())
+ {
+ BMCWEB_LOG_DEBUG << "TLS session: " << sp->uniqueId
+ << " will be used for this request.";
+ req->session = sp;
+ }
+#endif // BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
+
detail::middlewareCallHelper<
0U, decltype(ctx), decltype(*middlewares), Middlewares...>(
*middlewares, *req, res, ctx);
@@ -391,12 +536,18 @@ class Connection
}
void close()
{
-
if constexpr (std::is_same_v<Adaptor,
boost::beast::ssl_stream<
boost::asio::ip::tcp::socket>>)
{
adaptor.next_layer().close();
+#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
+ if (auto sp = session.lock())
+ {
+ BMCWEB_LOG_DEBUG << "Removing TLS session: " << sp->uniqueId;
+ persistent_data::SessionStore::getInstance().removeSession(sp);
+ }
+#endif // BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
}
else
{
@@ -652,6 +803,9 @@ class Connection
std::optional<crow::Request> req;
crow::Response res;
+#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
+ std::weak_ptr<crow::persistent_data::UserSession> session;
+#endif // BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
const std::string& serverName;
diff --git a/include/ssl_key_handler.hpp b/include/ssl_key_handler.hpp
index d634d63..2bd1f59 100644
--- a/include/ssl_key_handler.hpp
+++ b/include/ssl_key_handler.hpp
@@ -16,6 +16,7 @@
namespace ensuressl
{
+constexpr char const *trustStorePath = "/etc/ssl/certs/authority";
static void initOpenssl();
static EVP_PKEY *createEcKey();
@@ -312,7 +313,11 @@ inline std::shared_ptr<boost::asio::ssl::context>
boost::asio::ssl::context::no_tlsv1 |
boost::asio::ssl::context::no_tlsv1_1);
- // m_ssl_context.set_verify_mode(boost::asio::ssl::verify_peer);
+ mSslContext->set_verify_mode(boost::asio::ssl::verify_peer);
+
+ BMCWEB_LOG_DEBUG << "Using default TrustStore location: " << trustStorePath;
+ mSslContext->add_verify_path(trustStorePath);
+
mSslContext->use_certificate_file(ssl_pem_file,
boost::asio::ssl::context::pem);
mSslContext->use_private_key_file(ssl_pem_file,
OpenPOWER on IntegriCloud