This is an automated email from the ASF dual-hosted git repository.
maskit pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new c1f013551f Add support for cert verification to QUIC (#11787)
c1f013551f is described below
commit c1f013551fcc04886138acbc56c3ec3aee345d6f
Author: Masakazu Kitajo <[email protected]>
AuthorDate: Thu Sep 26 11:13:54 2024 -0600
Add support for cert verification to QUIC (#11787)
* Add support for cert verifycation to QUIC
* fix typos
---
include/iocore/net/TLSBasicSupport.h | 28 +++++++++++++++++++++++++
src/api/InkAPI.cc | 8 +++----
src/iocore/net/P_QUICNetVConnection.h | 4 ++++
src/iocore/net/P_SSLNetVConnection.h | 24 +--------------------
src/iocore/net/QUICNetVConnection.cc | 39 ++++++++++++++++++++++++++++++++++-
src/iocore/net/SSLClientUtils.cc | 13 ++++++------
src/iocore/net/SSLNetVConnection.cc | 21 +++++++++++++++++++
src/iocore/net/SSLUtils.cc | 14 ++++---------
src/iocore/net/TLSBasicSupport.cc | 17 +++++++++++++++
9 files changed, 123 insertions(+), 45 deletions(-)
diff --git a/include/iocore/net/TLSBasicSupport.h
b/include/iocore/net/TLSBasicSupport.h
index 4d64f4325c..c269c51c0f 100644
--- a/include/iocore/net/TLSBasicSupport.h
+++ b/include/iocore/net/TLSBasicSupport.h
@@ -47,11 +47,30 @@ public:
const char *get_tls_curve() const;
ink_hrtime get_tls_handshake_begin_time() const;
ink_hrtime get_tls_handshake_end_time() const;
+ /**
+ * Returns a certificate that need to be verified.
+ *
+ * Note: This function is only available when verify_certification is being
called.
+ * This function is probably just for TSVConnSslVerifyCTXGet.
+ * We could (and probably should) pass a cert to verify as an argument of
TS_EVENT_SSL_VERIFY_CLIENT/SERVER event.
+ */
+ X509_STORE_CTX *get_tls_cert_to_verify() const;
void set_valid_tls_version_min(int min);
void set_valid_tls_version_max(int max);
void set_valid_tls_protocols(unsigned long proto_mask, unsigned long
max_mask);
+ /**
+ * Give the plugin access to the data structure passed in during the
underlying
+ * openssl callback so the plugin can make more detailed decisions about the
+ * validity of the certificate in their cases.
+ *
+ * This function is supposed to be called from callback functions for TLS
libraries.
+ *
+ * @return 1 if verification failed
+ */
+ int verify_certificate(X509_STORE_CTX *ctx);
+
protected:
void clear();
@@ -61,9 +80,18 @@ protected:
void _record_tls_handshake_begin_time();
void _record_tls_handshake_end_time();
+ /**
+ * Implementation should schedule either TS_EVENT_SSL_VERIFY_SERVER or
TS_EVENT_SSL_VERIFY_CLIENT accordingly.
+ *
+ * @return 1 if verification failed
+ */
+ virtual int _verify_certificate(X509_STORE_CTX *ctx) = 0;
+
private:
static int _ex_data_index;
+ X509_STORE_CTX *_cert_to_verify = nullptr;
+
ink_hrtime _tls_handshake_begin_time = 0;
ink_hrtime _tls_handshake_end_time = 0;
};
diff --git a/src/api/InkAPI.cc b/src/api/InkAPI.cc
index cf957ac97f..b455de1abd 100644
--- a/src/api/InkAPI.cc
+++ b/src/api/InkAPI.cc
@@ -7950,10 +7950,10 @@ TSVConnSslSniGet(TSVConn sslp, int *length)
TSSslVerifyCTX
TSVConnSslVerifyCTXGet(TSVConn sslp)
{
- NetVConnection *vc = reinterpret_cast<NetVConnection *>(sslp);
- SSLNetVConnection *ssl_vc = dynamic_cast<SSLNetVConnection *>(vc);
- if (ssl_vc != nullptr) {
- return reinterpret_cast<TSSslVerifyCTX>(ssl_vc->get_verify_cert());
+ NetVConnection *vc = reinterpret_cast<NetVConnection *>(sslp);
+ TLSBasicSupport *tlsbs = vc->get_service<TLSBasicSupport>();
+ if (tlsbs != nullptr) {
+ return reinterpret_cast<TSSslVerifyCTX>(tlsbs->get_tls_cert_to_verify());
}
return nullptr;
}
diff --git a/src/iocore/net/P_QUICNetVConnection.h
b/src/iocore/net/P_QUICNetVConnection.h
index 019f870eaa..9797c24984 100644
--- a/src/iocore/net/P_QUICNetVConnection.h
+++ b/src/iocore/net/P_QUICNetVConnection.h
@@ -165,6 +165,7 @@ protected:
// TLSBasicSupport
SSL *_get_ssl_object() const override;
ssl_curve_id _get_tls_curve() const override;
+ int _verify_certificate(X509_STORE_CTX *ctx) override;
// TLSSNISupport
in_port_t _get_local_port() override;
@@ -235,6 +236,9 @@ private:
QUICStreamManager *_stream_manager = nullptr;
QUICApplicationMap *_application_map = nullptr;
+
+ bool _is_verifying_cert = false;
+ bool _is_cert_verified = false;
};
extern ClassAllocator<QUICNetVConnection> quicNetVCAllocator;
diff --git a/src/iocore/net/P_SSLNetVConnection.h
b/src/iocore/net/P_SSLNetVConnection.h
index edce1e1206..9b437a6d20 100644
--- a/src/iocore/net/P_SSLNetVConnection.h
+++ b/src/iocore/net/P_SSLNetVConnection.h
@@ -126,12 +126,6 @@ public:
return retval;
}
- SSLHandshakeStatus
- getSSLHandshakeStatus() const
- {
- return sslHandshakeStatus;
- }
-
bool
getSSLHandShakeComplete() const override
{
@@ -252,21 +246,6 @@ public:
bool protocol_mask_set = false;
unsigned long protocol_mask = 0;
- // Only applies during the VERIFY certificate hooks (client and server side)
- // Means to give the plugin access to the data structure passed in during
the underlying
- // openssl callback so the plugin can make more detailed decisions about the
- // validity of the certificate in their cases
- X509_STORE_CTX *
- get_verify_cert()
- {
- return verify_cert;
- }
- void
- set_verify_cert(X509_STORE_CTX *ctx)
- {
- verify_cert = ctx;
- }
-
bool
peer_provided_cert() const override
{
@@ -327,6 +306,7 @@ protected:
return this->ssl;
}
ssl_curve_id _get_tls_curve() const override;
+ int _verify_certificate(X509_STORE_CTX *ctx) override;
// TLSSessionResumptionSupport
const IpEndpoint &
@@ -376,8 +356,6 @@ private:
int64_t redoWriteSize = 0;
- X509_STORE_CTX *verify_cert = nullptr;
-
// Null-terminated string, or nullptr if there is no SNI server name.
std::unique_ptr<char[]> _ca_cert_file;
std::unique_ptr<char[]> _ca_cert_dir;
diff --git a/src/iocore/net/QUICNetVConnection.cc
b/src/iocore/net/QUICNetVConnection.cc
index d6c002221c..2fd225df24 100644
--- a/src/iocore/net/QUICNetVConnection.cc
+++ b/src/iocore/net/QUICNetVConnection.cc
@@ -769,8 +769,13 @@ QUICNetVConnection::get_quic_connection()
}
void
-QUICNetVConnection::reenable(int /* event ATS_UNUSED */)
+QUICNetVConnection::reenable(int event)
{
+ this->_is_verifying_cert = false;
+
+ if (event == TS_EVENT_ERROR) {
+ this->_is_cert_verified = false;
+ }
}
Continuation *
@@ -807,6 +812,38 @@ QUICNetVConnection::_get_tls_curve() const
}
}
+int
+QUICNetVConnection::_verify_certificate(X509_STORE_CTX * /* ctx ATS_UNUSED */)
+{
+ TSEvent eventId;
+ TSHttpHookID hookId;
+ APIHook *hook = nullptr;
+
+ // TODO Simply call callHooks once QUICNetVC implements TLSEventSupport
+ if (get_context() == NET_VCONNECTION_IN) {
+ eventId = TS_EVENT_SSL_VERIFY_CLIENT;
+ hookId = TS_SSL_VERIFY_CLIENT_HOOK;
+ } else {
+ eventId = TS_EVENT_SSL_VERIFY_SERVER;
+ hookId = TS_SSL_VERIFY_SERVER_HOOK;
+ }
+ hook = SSLAPIHooks::instance()->get(TSSslHookInternalID(hookId));
+ if (hook != nullptr) {
+ this->_is_verifying_cert = true;
+ WEAK_SCOPED_MUTEX_LOCK(lock, hook->m_cont->mutex, this_ethread());
+ hook->invoke(eventId, this);
+ }
+
+ // According to the implementation in SSLNetVC,
+ // we can assume that reenable() is called during the event handling.
+ ink_assert(this->_is_verifying_cert == false);
+ if (this->_is_cert_verified) {
+ return 1;
+ }
+
+ return 0;
+}
+
in_port_t
QUICNetVConnection::_get_local_port()
{
diff --git a/src/iocore/net/SSLClientUtils.cc b/src/iocore/net/SSLClientUtils.cc
index aef0ecf9f3..851351d2bf 100644
--- a/src/iocore/net/SSLClientUtils.cc
+++ b/src/iocore/net/SSLClientUtils.cc
@@ -130,15 +130,14 @@ verify_callback(int signature_ok, X509_STORE_CTX *ctx)
return !enforce_mode;
}
}
+
// If the previous configured checks passed, give the hook a try
- netvc->set_verify_cert(ctx);
- TLSEventSupport *es = TLSEventSupport::getInstance(ssl);
- if (es) {
- es->callHooks(TS_EVENT_SSL_VERIFY_SERVER);
+ TLSBasicSupport *tbs = TLSBasicSupport::getInstance(ssl);
+ if (tbs == nullptr) {
+ Dbg(dbg_ctl_ssl_verify, "call back on stale netvc");
+ return false;
}
- netvc->set_verify_cert(nullptr);
-
- if (netvc->getSSLHandshakeStatus() ==
SSLHandshakeStatus::SSL_HANDSHAKE_ERROR) {
+ if (tbs->verify_certificate(ctx) == 1) {
// Verify server hook failed and set the status to SSL_HANDSHAKE_ERROR
unsigned char *sni_name;
char buff[INET6_ADDRSTRLEN];
diff --git a/src/iocore/net/SSLNetVConnection.cc
b/src/iocore/net/SSLNetVConnection.cc
index 0ca851ebc8..d49c79ec28 100644
--- a/src/iocore/net/SSLNetVConnection.cc
+++ b/src/iocore/net/SSLNetVConnection.cc
@@ -1964,6 +1964,27 @@ SSLNetVConnection::_get_tls_curve() const
}
}
+int
+SSLNetVConnection::_verify_certificate(X509_STORE_CTX * /* ctx ATS_UNUSED */)
+{
+ // Currently, TS_EVENT_SSL_VERIFY_CLIENT/SERVER are invoked only with a
NetVC instance.
+ // This requires plugins to call TSSslVerifyCTX in their event handler.
+ // We could pass a structure that has both a cert to verify and a NetVC.
+ // It would allow us to remove confusing TSSslVerifyCTX and its internal
implementation that are only available during a very
+ // limited time.
+ if (get_context() == NET_VCONNECTION_IN) {
+ this->callHooks(TS_EVENT_SSL_VERIFY_CLIENT /* , ctx */);
+ } else {
+ this->callHooks(TS_EVENT_SSL_VERIFY_SERVER /* , ctx */);
+ }
+
+ if (this->sslHandshakeStatus == SSLHandshakeStatus::SSL_HANDSHAKE_ERROR) {
+ return 1;
+ }
+
+ return 0;
+}
+
ssl_error_t
SSLNetVConnection::_ssl_accept()
{
diff --git a/src/iocore/net/SSLUtils.cc b/src/iocore/net/SSLUtils.cc
index 95680bccb3..b519a0778f 100644
--- a/src/iocore/net/SSLUtils.cc
+++ b/src/iocore/net/SSLUtils.cc
@@ -283,20 +283,14 @@ ssl_verify_client_callback(int preverify_ok,
X509_STORE_CTX *ctx)
Dbg(dbg_ctl_ssl_verify, "Callback: verify client cert");
auto *ssl = static_cast<SSL
*>(X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
SSLNetVConnection *netvc = SSLNetVCAccess(ssl);
+ TLSBasicSupport *tbs = TLSBasicSupport::getInstance(ssl);
- if (!netvc || netvc->ssl != ssl) {
- Dbg(dbg_ctl_ssl_verify, "ssl_verify_client_callback call back on stale
netvc");
+ if (tbs == nullptr) {
+ Dbg(dbg_ctl_ssl_verify, "call back on stale netvc");
return false;
}
- netvc->set_verify_cert(ctx);
- TLSEventSupport *es = TLSEventSupport::getInstance(ssl);
- if (es) {
- es->callHooks(TS_EVENT_SSL_VERIFY_CLIENT);
- }
- netvc->set_verify_cert(nullptr);
-
- if (netvc->getSSLHandShakeComplete()) { // hook moved the handshake state to
terminal
+ if (tbs->verify_certificate(ctx) == 1) { // hook moved the handshake state
to terminal
Warning("TS_EVENT_SSL_VERIFY_CLIENT plugin failed the client certificate
check for %s.", netvc->options.sni_servername.get());
return false;
}
diff --git a/src/iocore/net/TLSBasicSupport.cc
b/src/iocore/net/TLSBasicSupport.cc
index d9b4d1e40b..723bdb16c2 100644
--- a/src/iocore/net/TLSBasicSupport.cc
+++ b/src/iocore/net/TLSBasicSupport.cc
@@ -166,6 +166,23 @@ TLSBasicSupport::set_valid_tls_version_max(int max)
SSL_set_max_proto_version(ssl, ver);
}
+int
+TLSBasicSupport::verify_certificate(X509_STORE_CTX *ctx)
+{
+ // See comments in SSLNetVConnection::_verify_certificate
+ this->_cert_to_verify = ctx;
+ int ret = this->_verify_certificate(ctx);
+ this->_cert_to_verify = nullptr;
+
+ return ret;
+}
+
+X509_STORE_CTX *
+TLSBasicSupport::get_tls_cert_to_verify() const
+{
+ return this->_cert_to_verify;
+}
+
void
TLSBasicSupport::_record_tls_handshake_begin_time()
{