From 689f3ede7d66c64618599d8b14869ed3d7df043f Mon Sep 17 00:00:00 2001 From: James Yonan Date: Wed, 18 Dec 2024 13:56:44 -0700 Subject: [PATCH] OpenSSLContext::Config: added set_cn_reject_handler() hook The set_cn_reject_handler() method allows users to specify a hook for examining the leaf Common Name during SSL/TLS handshakes, with the option to reject the handshake. Signed-off-by: James Yonan --- openvpn/mbedtls/ssl/sslctx.hpp | 5 ++++ openvpn/openssl/ssl/sslctx.hpp | 36 +++++++++++++++++++++++++++- openvpn/ssl/cn_reject_handler.hpp | 40 +++++++++++++++++++++++++++++++ openvpn/ssl/sslapi.hpp | 2 ++ 4 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 openvpn/ssl/cn_reject_handler.hpp diff --git a/openvpn/mbedtls/ssl/sslctx.hpp b/openvpn/mbedtls/ssl/sslctx.hpp index f98ad8e90..84028b05c 100644 --- a/openvpn/mbedtls/ssl/sslctx.hpp +++ b/openvpn/mbedtls/ssl/sslctx.hpp @@ -261,6 +261,11 @@ class MbedTLSContext : public SSLFactoryAPI throw MbedTLSException("set_sni_name not implemented"); } + void set_cn_reject_handler(CommonNameReject *cn_reject_handler_arg) override + { + throw MbedTLSException("set_cn_reject_handler not implemented"); + } + void set_private_key_password(const std::string &pwd) override { priv_key_pwd = pwd; diff --git a/openvpn/openssl/ssl/sslctx.hpp b/openvpn/openssl/ssl/sslctx.hpp index a484ee7ea..3ab9d85f6 100644 --- a/openvpn/openssl/ssl/sslctx.hpp +++ b/openvpn/openssl/ssl/sslctx.hpp @@ -193,6 +193,18 @@ class OpenSSLContext : public SSLFactoryAPI sni_name = sni_name_arg; } + /** + * Add a hook to allow inspection and possible rejection + * of leaf cert common names (server-side only). + * + * @param cn_reject_handler_arg CommonNameReject object + * that implements a custom reject() hook. + */ + void set_cn_reject_handler(CommonNameReject *cn_reject_handler_arg) override + { + cn_reject_handler = cn_reject_handler_arg; + } + void set_private_key_password(const std::string &pwd) override { pkey.set_private_key_password(pwd); @@ -664,6 +676,7 @@ class OpenSSLContext : public SSLFactoryAPI std::string external_pki_alias; TLSSessionTicketBase *session_ticket_handler = nullptr; // server side only SNI::HandlerBase *sni_handler = nullptr; // server side only + CommonNameReject *cn_reject_handler = nullptr; Frame::Ptr frame; unsigned int flags = 0; // defined in sslconsts.hpp std::string sni_name; // client side only @@ -1999,10 +2012,31 @@ class OpenSSLContext : public SSLFactoryAPI preverify_ok = false; } + // get the Common name + std::string cn = OpenSSLPKI::x509_get_field(current_cert, NID_commonName); + + // early rejection of Common Name? + if (self->config->cn_reject_handler) + { + try + { + if (self->config->cn_reject_handler->reject(cn)) + { + OVPN_LOG_INFO("VERIFY FAIL -- early rejection of leaf cert Common Name"); + preverify_ok = false; + } + } + catch (const std::exception &e) + { + OVPN_LOG_INFO("VERIFY FAIL -- early rejection of leaf cert Common Name due to handler exception: " << e.what()); + preverify_ok = false; + } + } + if (self_ssl->authcert) { // save the Common Name - self_ssl->authcert->cn = OpenSSLPKI::x509_get_field(current_cert, NID_commonName); + self_ssl->authcert->cn = std::move(cn); // NOTE: cn is consumed! // save the leaf cert serial number load_serial_number_into_authcert(*self_ssl->authcert, current_cert); diff --git a/openvpn/ssl/cn_reject_handler.hpp b/openvpn/ssl/cn_reject_handler.hpp new file mode 100644 index 000000000..1fff62c6a --- /dev/null +++ b/openvpn/ssl/cn_reject_handler.hpp @@ -0,0 +1,40 @@ +// OpenVPN -- An application to securely tunnel IP networks +// over a single port, with support for SSL/TLS-based +// session authentication and key exchange, +// packet encryption, packet authentication, and +// packet compression. +// +// Copyright (C) 2012- OpenVPN Inc. +// +// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception +// + +#pragma once + +#include +#include + +namespace openvpn { + +/** + * Abstract base class used to provide early rejection + * of specific Common Names during SSL/TLS handshake. + */ +class CommonNameReject +{ + public: + typedef std::unique_ptr UPtr; + + /** + * Should a leaf certificate having Common Name cn + * be rejected during SSL/TLS handshake? + * + * @param cn Common Name + * @return true if certificate should be rejected. + */ + virtual bool reject(const std::string &cn) = 0; + + virtual ~CommonNameReject() = default; +}; + +} // namespace openvpn diff --git a/openvpn/ssl/sslapi.hpp b/openvpn/ssl/sslapi.hpp index d0c1e80fb..6c1ecaa2f 100644 --- a/openvpn/ssl/sslapi.hpp +++ b/openvpn/ssl/sslapi.hpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include "openvpn/log/logger.hpp" @@ -146,6 +147,7 @@ class SSLConfigAPI : public RC virtual void set_sni_handler(SNI::HandlerBase *sni_handler) = 0; // server side virtual void set_sni_name(const std::string &sni_name_arg) = 0; // client side virtual void set_private_key_password(const std::string &pwd) = 0; + virtual void set_cn_reject_handler(CommonNameReject *cn_reject_handler_arg) = 0; virtual void load_ca(const std::string &ca_txt, bool strict) = 0; virtual void load_crl(const std::string &crl_txt) = 0; virtual void load_cert(const std::string &cert_txt) = 0;