This is an automated email from the ASF dual-hosted git repository.
shinrich 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 53fd833 Added TS_SSL_CLIENT_HELLO_HOOK and docs Added new test for
client hello hook
53fd833 is described below
commit 53fd833f6b0d3022def83cd3b6f079a2dc945699
Author: dyrock <[email protected]>
AuthorDate: Mon Feb 4 16:04:08 2019 -0600
Added TS_SSL_CLIENT_HELLO_HOOK and docs
Added new test for client hello hook
---
.../hooks-and-transactions/ssl-hooks.en.rst | 21 ++++-
include/ts/apidefs.h.in | 2 +
iocore/net/P_SSLNetVConnection.h | 16 +++-
iocore/net/P_SSLUtils.h | 1 +
iocore/net/SSLNetVConnection.cc | 82 +++++++++++++++---
iocore/net/SSLUtils.cc | 12 ++-
proxy/InkAPIInternal.h | 1 +
proxy/http/HttpDebugNames.cc | 4 +
src/traffic_server/InkAPITest.cc | 1 +
.../gold_tests/tls_hooks/gold/client-hello-1.gold | 0
.../gold_tests/tls_hooks/gold/ts-cert-1-im-2.gold | 2 +-
tests/gold_tests/tls_hooks/gold/ts-cert-1.gold | 2 +-
tests/gold_tests/tls_hooks/gold/ts-cert-2.gold | 2 +-
tests/gold_tests/tls_hooks/gold/ts-cert-im-1.gold | 2 +-
.../tls_hooks/gold/ts-client-hello-1.gold | 3 +
.../tls_hooks/gold/ts-client-hello-2.gold | 5 ++
.../tls_hooks/gold/ts-client-hello-delayed-1.gold | 4 +
.../tls_hooks/gold/ts-out-delay-start-2.gold | 2 +-
.../gold_tests/tls_hooks/gold/ts-preaccept-1.gold | 2 +-
.../gold_tests/tls_hooks/gold/ts-preaccept-2.gold | 2 +-
.../gold/ts-preaccept-delayed-1-immdate-2.gold | 2 +-
.../tls_hooks/gold/ts-preaccept-delayed-1.gold | 2 +-
.../tls_hooks/gold/ts-preaccept1-sni1-cert1.gold | 2 +-
tests/gold_tests/tls_hooks/gold/ts-sni-1.gold | 2 +-
tests/gold_tests/tls_hooks/gold/ts-sni-2.gold | 2 +-
tests/gold_tests/tls_hooks/tls_hooks16.test.py | 79 +++++++++++++++++
tests/gold_tests/tls_hooks/tls_hooks17.test.py | 79 +++++++++++++++++
tests/gold_tests/tls_hooks/tls_hooks18.test.py | 83 ++++++++++++++++++
tests/tools/plugins/ssl_hook_test.cc | 99 +++++++++++++++++-----
tests/tools/plugins/test_hooks.cc | 2 -
30 files changed, 463 insertions(+), 55 deletions(-)
diff --git
a/doc/developer-guide/plugins/hooks-and-transactions/ssl-hooks.en.rst
b/doc/developer-guide/plugins/hooks-and-transactions/ssl-hooks.en.rst
index a21e636..1b7d49c 100644
--- a/doc/developer-guide/plugins/hooks-and-transactions/ssl-hooks.en.rst
+++ b/doc/developer-guide/plugins/hooks-and-transactions/ssl-hooks.en.rst
@@ -60,6 +60,12 @@ TS_VCONN_CLOSE_HOOK
This hook is invoked after the SSL handshake is done and when the IO is
closing. The TSVConnArgs should be cleaned up here. A callback at this point
must reenable.
+TS_SSL_CLIENT_HELLO_HOOK
+------------------------
+This hook is called when the client hello arrived for the TLS handshake. If
called it will always be called after TS_VCONN_START_HOOK. The plugin callback
can execute code to examine client hello information.
+
+TLS handshake processing will pause until the hook callback executes
:c:func:`TSVConnReenable()`.
+
TS_SSL_SERVERNAME_HOOK
----------------------
@@ -87,7 +93,7 @@ a certificate.
TS_SSL_VERIFY_CLIENT_HOOK
-------------------------
-This hook is called when a client connects to Traffic Server and presents a
+This hook is called when a client connects to Traffic Server and presents a
client certificate in the case of a mutual TLS handshake. The callback can
get the SSL object from the TSVConn argument and use that to access the client
certificate and make any additional checks.
@@ -110,7 +116,7 @@ for pausing processing during the certificate verify
callback.
TS_VCONN_OUTBOUND_START_HOOK
----------------------------
-This hook is invoked after ATS has connected to the upstream server and before
the SSL handshake has started. This gives the plugin the option of
+This hook is invoked after ATS has connected to the upstream server and before
the SSL handshake has started. This gives the plugin the option of
overriding the default SSL connection options on the SSL object.
In theory this hook could apply and be useful for non-SSL connections as well,
but at this point this hook is only called in the SSL sequence.
@@ -137,14 +143,19 @@ TLS Inbound Hook State Diagram
TS_VCONN_START_HOOK -> HANDSHAKE_HOOKS_PRE_INVOKE;
HANDSHAKE_HOOKS_PRE_INVOKE -> TSVConnReenable;
TSVConnReenable -> HANDSHAKE_HOOKS_PRE;
+ TS_SSL_CLIENT_HELLO_HOOK -> HANDSHAKE_HOOKS_CLIENT_HELLO_INVOKE;
+ HANDSHAKE_HOOKS_CLIENT_HELLO_INVOKE -> TSVConnReenable2;
+ TSVConnReenable2 -> HANDSHAKE_HOOKS_CLIENT_HELLO;
+ HANDSHAKE_HOOKS_CLIENT_HELLO -> TS_SSL_CLIENT_HELLO_HOOK;
+ HANDSHAKE_HOOKS_CLIENT_HELLO -> TS_SSL_SERVERNAME_HOOK;
TS_SSL_SERVERNAME_HOOK -> HANDSHAKE_HOOKS_SNI;
HANDSHAKE_HOOKS_SNI -> TS_SSL_SERVERNAME_HOOK;
HANDSHAKE_HOOKS_SNI -> TS_SSL_CERT_HOOK;
HANDSHAKE_HOOKS_SNI -> HANDSHAKE_HOOKS_DONE;
HANDSHAKE_HOOKS_CERT -> TS_SSL_CERT_HOOK;
TS_SSL_CERT_HOOK -> HANDSHAKE_HOOKS_CERT_INVOKE;
- HANDSHAKE_HOOKS_CERT_INVOKE -> TSVConnReenable2;
- TSVConnReenable2 -> HANDSHAKE_HOOKS_CERT;
+ HANDSHAKE_HOOKS_CERT_INVOKE -> TSVConnReenable3;
+ TSVConnReenable3 -> HANDSHAKE_HOOKS_CERT;
HANDSHAKE_HOOKS_CERT -> TS_SSL_VERIFY_CLIENT_HOOK;
HANDSHAKE_HOOKS_SNI -> TS_SSL_VERIFY_CLIENT_HOOK;
@@ -159,6 +170,8 @@ TLS Inbound Hook State Diagram
HANDSHAKE_HOOKS_PRE [shape=box];
HANDSHAKE_HOOKS_PRE_INVOKE [shape=box];
+ HANDSHAKE_HOOKS_CLIENT_HELLO [shape=box];
+ HANDSHAKE_HOOKS_CLIENT_HELLO_INVOKE [shape=box];
HANDSHAKE_HOOKS_SNI [shape=box];
HANDSHAKE_HOOKS_VERIFY [shape=box];
HANDSHAKE_HOOKS_CERT [shape=box];
diff --git a/include/ts/apidefs.h.in b/include/ts/apidefs.h.in
index 7adf619..2b1e5d2 100644
--- a/include/ts/apidefs.h.in
+++ b/include/ts/apidefs.h.in
@@ -286,6 +286,7 @@ typedef enum {
TS_VCONN_START_HOOK = TS_SSL_FIRST_HOOK,
TS_VCONN_PRE_ACCEPT_HOOK = TS_VCONN_START_HOOK, // Deprecated but compatible
for now.
TS_VCONN_CLOSE_HOOK,
+ TS_SSL_CLIENT_HELLO_HOOK,
TS_SSL_SNI_HOOK,
TS_SSL_CERT_HOOK = TS_SSL_SNI_HOOK,
TS_SSL_SERVERNAME_HOOK,
@@ -494,6 +495,7 @@ typedef enum {
TS_EVENT_SSL_SERVERNAME = 60204,
TS_EVENT_SSL_VERIFY_SERVER = 60205,
TS_EVENT_SSL_VERIFY_CLIENT = 60206,
+ TS_EVENT_SSL_CLIENT_HELLO = 60207,
TS_EVENT_MGMT_UPDATE = 60300
} TSEvent;
diff --git a/iocore/net/P_SSLNetVConnection.h b/iocore/net/P_SSLNetVConnection.h
index 4de0038..25e84b5 100644
--- a/iocore/net/P_SSLNetVConnection.h
+++ b/iocore/net/P_SSLNetVConnection.h
@@ -241,9 +241,19 @@ public:
}
}
break;
- case HANDSHAKE_HOOKS_SNI:
+ case HANDSHAKE_HOOKS_CLIENT_HELLO:
+ case HANDSHAKE_HOOKS_CLIENT_HELLO_INVOKE:
if (eventId == TS_EVENT_VCONN_START) {
retval = true;
+ } else if (eventId == TS_EVENT_SSL_CLIENT_HELLO) {
+ if (curHook) {
+ retval = true;
+ }
+ }
+ break;
+ case HANDSHAKE_HOOKS_SNI:
+ if (eventId == TS_EVENT_VCONN_START || eventId ==
TS_EVENT_SSL_CLIENT_HELLO) {
+ retval = true;
} else if (eventId == TS_EVENT_SSL_SERVERNAME) {
if (curHook) {
retval = true;
@@ -252,7 +262,7 @@ public:
break;
case HANDSHAKE_HOOKS_CERT:
case HANDSHAKE_HOOKS_CERT_INVOKE:
- if (eventId == TS_EVENT_VCONN_START || eventId ==
TS_EVENT_SSL_SERVERNAME) {
+ if (eventId == TS_EVENT_VCONN_START || eventId ==
TS_EVENT_SSL_CLIENT_HELLO || eventId == TS_EVENT_SSL_SERVERNAME) {
retval = true;
} else if (eventId == TS_EVENT_SSL_CERT) {
if (curHook) {
@@ -394,6 +404,8 @@ private:
enum SSLHandshakeHookState {
HANDSHAKE_HOOKS_PRE,
HANDSHAKE_HOOKS_PRE_INVOKE,
+ HANDSHAKE_HOOKS_CLIENT_HELLO,
+ HANDSHAKE_HOOKS_CLIENT_HELLO_INVOKE,
HANDSHAKE_HOOKS_SNI,
HANDSHAKE_HOOKS_CERT,
HANDSHAKE_HOOKS_CERT_INVOKE,
diff --git a/iocore/net/P_SSLUtils.h b/iocore/net/P_SSLUtils.h
index 6990db6..e70dbc5 100644
--- a/iocore/net/P_SSLUtils.h
+++ b/iocore/net/P_SSLUtils.h
@@ -85,6 +85,7 @@ enum SSL_Stats {
/* error stats */
ssl_error_want_write,
ssl_error_want_read,
+ ssl_error_want_client_hello_cb,
ssl_error_want_x509_lookup,
ssl_error_syscall,
ssl_error_read_eos,
diff --git a/iocore/net/SSLNetVConnection.cc b/iocore/net/SSLNetVConnection.cc
index 14712ee..5d08d3d 100644
--- a/iocore/net/SSLNetVConnection.cc
+++ b/iocore/net/SSLNetVConnection.cc
@@ -265,6 +265,13 @@ ssl_read_from_net(SSLNetVConnection *sslvc, EThread
*lthread, int64_t &ret)
SSL_INCREMENT_DYN_STAT(ssl_error_want_read);
Debug("ssl.error", "[SSL_NetVConnection::ssl_read_from_net]
SSL_ERROR_WOULD_BLOCK(read)");
break;
+#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB
+ case SSL_ERROR_WANT_CLIENT_HELLO_CB:
+ event = SSL_READ_WOULD_BLOCK;
+ SSL_INCREMENT_DYN_STAT(ssl_error_want_client_hello_cb);
+ Debug("ssl.error", "[SSL_NetVConnection::ssl_read_from_net]
SSL_ERROR_WOULD_BLOCK(read/client hello cb)");
+ break;
+#endif
case SSL_ERROR_WANT_X509_LOOKUP:
event = SSL_READ_WOULD_BLOCK;
SSL_INCREMENT_DYN_STAT(ssl_error_want_x509_lookup);
@@ -806,6 +813,9 @@ SSLNetVConnection::load_buffer_and_write(int64_t towrite,
MIOBufferAccessor &buf
Debug("ssl.error", "SSL_write-SSL_ERROR_WANT_READ");
break;
case SSL_ERROR_WANT_WRITE:
+#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB
+ case SSL_ERROR_WANT_CLIENT_HELLO_CB:
+#endif
case SSL_ERROR_WANT_X509_LOOKUP: {
if (SSL_ERROR_WANT_WRITE == err) {
SSL_INCREMENT_DYN_STAT(ssl_error_want_write);
@@ -813,7 +823,11 @@ SSLNetVConnection::load_buffer_and_write(int64_t towrite,
MIOBufferAccessor &buf
} else if (SSL_ERROR_WANT_X509_LOOKUP == err) {
SSL_INCREMENT_DYN_STAT(ssl_error_want_x509_lookup);
}
-
+#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB
+ else if (SSL_ERROR_WANT_CLIENT_HELLO_CB == err) {
+ SSL_INCREMENT_DYN_STAT(ssl_error_want_client_hello_cb);
+ }
+#endif
needs |= EVENTIO_WRITE;
num_really_written = -EAGAIN;
Debug("ssl.error", "SSL_write-SSL_ERROR_WANT_WRITE");
@@ -1098,7 +1112,7 @@ SSLNetVConnection::sslServerHandShakeEvent(int &err)
{
// Continue on if we are in the invoked state. The hook has not yet
reenabled
if (sslHandshakeHookState == HANDSHAKE_HOOKS_CERT_INVOKE ||
sslHandshakeHookState == HANDSHAKE_HOOKS_CLIENT_CERT_INVOKE ||
- sslHandshakeHookState == HANDSHAKE_HOOKS_PRE_INVOKE) {
+ sslHandshakeHookState == HANDSHAKE_HOOKS_PRE_INVOKE ||
sslHandshakeHookState == HANDSHAKE_HOOKS_CLIENT_HELLO_INVOKE) {
return SSL_WAIT_FOR_HOOK;
}
@@ -1110,9 +1124,10 @@ SSLNetVConnection::sslServerHandShakeEvent(int &err)
} else {
curHook = curHook->next();
}
- // If no more hooks, move onto SNI
+ // If no more hooks, move onto CLIENT HELLO
+
if (nullptr == curHook) {
- sslHandshakeHookState = HANDSHAKE_HOOKS_SNI;
+ sslHandshakeHookState = HANDSHAKE_HOOKS_CLIENT_HELLO;
} else {
sslHandshakeHookState = HANDSHAKE_HOOKS_PRE_INVOKE;
ContWrapper::wrap(nh->mutex.get(), curHook->m_cont,
TS_EVENT_VCONN_START, this);
@@ -1294,7 +1309,10 @@ SSLNetVConnection::sslServerHandShakeEvent(int &err)
case SSL_ERROR_WANT_READ:
return SSL_HANDSHAKE_WANT_READ;
-
+#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB
+ case SSL_ERROR_WANT_CLIENT_HELLO_CB:
+ return EVENT_CONT;
+#endif
// This value is only defined in openssl has been patched to
// enable the sni callback to break out of the SSL_accept processing
#ifdef SSL_ERROR_WANT_SNI_RESOLVE
@@ -1405,7 +1423,12 @@ SSLNetVConnection::sslClientHandShakeEvent(int &err)
SSL_INCREMENT_DYN_STAT(ssl_error_want_read);
Debug("ssl.error", "SSLNetVConnection::sslClientHandShakeEvent,
SSL_ERROR_WANT_READ");
return SSL_HANDSHAKE_WANT_READ;
-
+#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB
+ case SSL_ERROR_WANT_CLIENT_HELLO_CB:
+ SSL_INCREMENT_DYN_STAT(ssl_error_want_client_hello_cb);
+ Debug("ssl.error", "SSLNetVConnection::sslClientHandShakeEvent,
SSL_ERROR_WANT_CLIENT_HELLO_CB");
+ break;
+#endif
case SSL_ERROR_WANT_X509_LOOKUP:
SSL_INCREMENT_DYN_STAT(ssl_error_want_x509_lookup);
Debug("ssl.error", "SSLNetVConnection::sslClientHandShakeEvent,
SSL_ERROR_WANT_X509_LOOKUP");
@@ -1516,6 +1539,9 @@ SSLNetVConnection::reenable(NetHandler *nh, int event)
case HANDSHAKE_HOOKS_OUTBOUND_PRE_INVOKE:
sslHandshakeHookState = HANDSHAKE_HOOKS_OUTBOUND_PRE;
break;
+ case HANDSHAKE_HOOKS_CLIENT_HELLO_INVOKE:
+ sslHandshakeHookState = HANDSHAKE_HOOKS_CLIENT_HELLO;
+ break;
case HANDSHAKE_HOOKS_CERT_INVOKE:
sslHandshakeHookState = HANDSHAKE_HOOKS_CERT;
break;
@@ -1541,7 +1567,10 @@ SSLNetVConnection::reenable(NetHandler *nh, int event)
}
if (curHook != nullptr) {
// Invoke the hook and return, wait for next reenable
- if (sslHandshakeHookState == HANDSHAKE_HOOKS_CLIENT_CERT) {
+ if (sslHandshakeHookState == HANDSHAKE_HOOKS_CLIENT_HELLO) {
+ sslHandshakeHookState = HANDSHAKE_HOOKS_CLIENT_HELLO_INVOKE;
+ curHook->invoke(TS_EVENT_SSL_CLIENT_HELLO, this);
+ } else if (sslHandshakeHookState == HANDSHAKE_HOOKS_CLIENT_CERT) {
sslHandshakeHookState = HANDSHAKE_HOOKS_CLIENT_CERT_INVOKE;
curHook->invoke(TS_EVENT_SSL_VERIFY_CLIENT, this);
} else if (sslHandshakeHookState == HANDSHAKE_HOOKS_CERT) {
@@ -1573,6 +1602,10 @@ SSLNetVConnection::reenable(NetHandler *nh, int event)
switch (this->sslHandshakeHookState) {
case HANDSHAKE_HOOKS_PRE:
case HANDSHAKE_HOOKS_PRE_INVOKE:
+ sslHandshakeHookState = HANDSHAKE_HOOKS_CLIENT_HELLO;
+ break;
+ case HANDSHAKE_HOOKS_CLIENT_HELLO:
+ case HANDSHAKE_HOOKS_CLIENT_HELLO_INVOKE:
sslHandshakeHookState = HANDSHAKE_HOOKS_SNI;
break;
case HANDSHAKE_HOOKS_SNI:
@@ -1620,15 +1653,18 @@ bool
SSLNetVConnection::callHooks(TSEvent eventId)
{
// Only dealing with the SNI/CERT hook so far.
- ink_assert(eventId == TS_EVENT_SSL_CERT || eventId ==
TS_EVENT_SSL_SERVERNAME || eventId == TS_EVENT_SSL_VERIFY_SERVER ||
- eventId == TS_EVENT_SSL_VERIFY_CLIENT || eventId ==
TS_EVENT_VCONN_CLOSE || eventId == TS_EVENT_VCONN_OUTBOUND_CLOSE);
+ ink_assert(eventId == TS_EVENT_SSL_CLIENT_HELLO || eventId ==
TS_EVENT_SSL_CERT || eventId == TS_EVENT_SSL_SERVERNAME ||
+ eventId == TS_EVENT_SSL_VERIFY_SERVER || eventId ==
TS_EVENT_SSL_VERIFY_CLIENT || eventId == TS_EVENT_VCONN_CLOSE ||
+ eventId == TS_EVENT_VCONN_OUTBOUND_CLOSE);
Debug("ssl", "callHooks sslHandshakeHookState=%d eventID=%d",
this->sslHandshakeHookState, eventId);
// Move state if it is appropriate
switch (this->sslHandshakeHookState) {
case HANDSHAKE_HOOKS_PRE:
case HANDSHAKE_HOOKS_OUTBOUND_PRE:
- if (eventId == TS_EVENT_SSL_SERVERNAME) {
+ if (eventId == TS_EVENT_SSL_CLIENT_HELLO) {
+ this->sslHandshakeHookState = HANDSHAKE_HOOKS_CLIENT_HELLO;
+ } else if (eventId == TS_EVENT_SSL_SERVERNAME) {
this->sslHandshakeHookState = HANDSHAKE_HOOKS_SNI;
} else if (eventId == TS_EVENT_SSL_VERIFY_SERVER) {
this->sslHandshakeHookState = HANDSHAKE_HOOKS_VERIFY_SERVER;
@@ -1636,6 +1672,16 @@ SSLNetVConnection::callHooks(TSEvent eventId)
this->sslHandshakeHookState = HANDSHAKE_HOOKS_CERT;
}
break;
+ case HANDSHAKE_HOOKS_CLIENT_HELLO:
+ if (eventId == TS_EVENT_SSL_SERVERNAME) {
+ this->sslHandshakeHookState = HANDSHAKE_HOOKS_SNI;
+ } else if (eventId == TS_EVENT_SSL_CERT) {
+ this->sslHandshakeHookState = HANDSHAKE_HOOKS_CERT;
+ } else if (eventId == TS_EVENT_VCONN_CLOSE) {
+ // Jump to the end
+ this->sslHandshakeHookState = HANDSHAKE_HOOKS_DONE;
+ }
+ break;
case HANDSHAKE_HOOKS_SNI:
if (eventId == TS_EVENT_SSL_CERT) {
this->sslHandshakeHookState = HANDSHAKE_HOOKS_CERT;
@@ -1650,6 +1696,19 @@ SSLNetVConnection::callHooks(TSEvent eventId)
// Look for hooks associated with the event
switch (this->sslHandshakeHookState) {
+ case HANDSHAKE_HOOKS_CLIENT_HELLO:
+ case HANDSHAKE_HOOKS_CLIENT_HELLO_INVOKE:
+ if (!curHook) {
+ curHook = ssl_hooks->get(TS_SSL_CLIENT_HELLO_INTERNAL_HOOK);
+ } else {
+ curHook = curHook->next();
+ }
+ if (curHook == nullptr) {
+ this->sslHandshakeHookState = HANDSHAKE_HOOKS_SNI;
+ } else {
+ this->sslHandshakeHookState = HANDSHAKE_HOOKS_CLIENT_HELLO_INVOKE;
+ }
+ break;
case HANDSHAKE_HOOKS_VERIFY_SERVER:
// The server verify event addresses ATS to origin handshake
// All the other events are for client to ATS
@@ -1731,7 +1790,8 @@ SSLNetVConnection::callHooks(TSEvent eventId)
if (curHook != nullptr) {
curHook->invoke(eventId, this);
reenabled =
- (this->sslHandshakeHookState != HANDSHAKE_HOOKS_CERT_INVOKE &&
this->sslHandshakeHookState != HANDSHAKE_HOOKS_PRE_INVOKE);
+ (this->sslHandshakeHookState != HANDSHAKE_HOOKS_CERT_INVOKE &&
this->sslHandshakeHookState != HANDSHAKE_HOOKS_PRE_INVOKE &&
+ this->sslHandshakeHookState != HANDSHAKE_HOOKS_CLIENT_HELLO_INVOKE);
Debug("ssl", "Called hook on state=%d reenabled=%d",
sslHandshakeHookState, reenabled);
}
diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc
index 53255b1..305317a 100644
--- a/iocore/net/SSLUtils.cc
+++ b/iocore/net/SSLUtils.cc
@@ -423,7 +423,8 @@ PerformAction(Continuation *cont, const char *servername)
static int
ssl_client_hello_callback(SSL *s, int *al, void *arg)
{
- const char *servername = nullptr;
+ SSLNetVConnection *netvc = SSLNetVCAccess(s);
+ const char *servername = nullptr;
const unsigned char *p;
size_t remaining, len;
// Parse the servrer name if the get extension call succeeds and there are
more than 2 bytes to parse
@@ -452,9 +453,6 @@ ssl_client_hello_callback(SSL *s, int *al, void *arg)
}
}
}
-
- SSLNetVConnection *netvc = SSLNetVCAccess(s);
-
netvc->serverName = servername ? servername : "";
int ret = PerformAction(netvc, netvc->serverName);
if (ret != SSL_TLSEXT_ERR_OK) {
@@ -466,6 +464,12 @@ ssl_client_hello_callback(SSL *s, int *al, void *arg)
if (netvc->protocol_mask_set) {
setTLSValidProtocols(s, netvc->protocol_mask, TLSValidProtocols::max_mask);
}
+
+ bool reenabled = netvc->callHooks(TS_EVENT_SSL_CLIENT_HELLO);
+
+ if (!reenabled) {
+ return SSL_CLIENT_HELLO_RETRY;
+ }
return SSL_CLIENT_HELLO_SUCCESS;
}
#endif
diff --git a/proxy/InkAPIInternal.h b/proxy/InkAPIInternal.h
index cd75d64..7955c53 100644
--- a/proxy/InkAPIInternal.h
+++ b/proxy/InkAPIInternal.h
@@ -269,6 +269,7 @@ typedef enum {
TS_SSL_INTERNAL_FIRST_HOOK,
TS_VCONN_START_INTERNAL_HOOK = TS_SSL_INTERNAL_FIRST_HOOK,
TS_VCONN_CLOSE_INTERNAL_HOOK,
+ TS_SSL_CLIENT_HELLO_INTERNAL_HOOK,
TS_SSL_CERT_INTERNAL_HOOK,
TS_SSL_SERVERNAME_INTERNAL_HOOK,
TS_SSL_VERIFY_SERVER_INTERNAL_HOOK,
diff --git a/proxy/http/HttpDebugNames.cc b/proxy/http/HttpDebugNames.cc
index c76ef0e..f99e5b1 100644
--- a/proxy/http/HttpDebugNames.cc
+++ b/proxy/http/HttpDebugNames.cc
@@ -365,6 +365,8 @@ HttpDebugNames::get_event_name(int event)
return "TS_EVENT_INTERNAL_60201";
case TS_EVENT_INTERNAL_60202:
return "TS_EVENT_INTERNAL_60202";
+ case TS_EVENT_SSL_CLIENT_HELLO:
+ return "TS_EVENT_SSL_CLIENT_HELLO";
case TS_EVENT_SSL_CERT:
return "TS_EVENT_SSL_CERT";
case TS_EVENT_SSL_SERVERNAME:
@@ -607,6 +609,8 @@ HttpDebugNames::get_api_hook_name(TSHttpHookID t)
return "TS_VCONN_START_HOOK";
case TS_VCONN_CLOSE_HOOK:
return "TS_VCONN_CLOSE_HOOK";
+ case TS_SSL_CLIENT_HELLO_HOOK:
+ return "TS_SSL_CLIENT_HELLO_HOOK";
case TS_SSL_CERT_HOOK:
return "TS_SSL_CERT_HOOK";
case TS_SSL_SERVERNAME_HOOK:
diff --git a/src/traffic_server/InkAPITest.cc b/src/traffic_server/InkAPITest.cc
index dcf7a84..d000838 100644
--- a/src/traffic_server/InkAPITest.cc
+++ b/src/traffic_server/InkAPITest.cc
@@ -6620,6 +6620,7 @@ typedef enum {
ORIG_TS_SSL_FIRST_HOOK,
ORIG_TS_VCONN_START_HOOK = ORIG_TS_SSL_FIRST_HOOK,
ORIG_TS_VCONN_CLOSE_HOOK,
+ ORIG_TS_SSL_CLIENT_HELLO_HOOK,
ORIG_TS_SSL_SNI_HOOK,
ORIG_TS_SSL_SERVERNAME_HOOK,
ORIG_TS_SSL_VERIFY_SERVER_HOOK,
diff --git a/tests/gold_tests/tls_hooks/gold/client-hello-1.gold
b/tests/gold_tests/tls_hooks/gold/client-hello-1.gold
new file mode 100644
index 0000000..e69de29
diff --git a/tests/gold_tests/tls_hooks/gold/ts-cert-1-im-2.gold
b/tests/gold_tests/tls_hooks/gold/ts-cert-1-im-2.gold
index a2ec9b7..98c8ce5 100644
--- a/tests/gold_tests/tls_hooks/gold/ts-cert-1-im-2.gold
+++ b/tests/gold_tests/tls_hooks/gold/ts-cert-1-im-2.gold
@@ -1,4 +1,4 @@
-`` DIAG: (ssl_hook_test) Setup callbacks pa=0 sni=0 cert=1 cert_imm=2
pa_delay=0
+`` DIAG: (ssl_hook_test) Setup callbacks pa=0 client_hello=0
client_hello_imm=0 sni=0 cert=1 cert_imm=2 pa_delay=0
`` DIAG: (ssl_hook_test) Cert callback 0 ssl_vc=``
`` DIAG: (ssl_hook_test) Callback reenable ssl_vc=``
`` DIAG: (ssl_hook_test) Cert callback 0 ssl_vc=``
diff --git a/tests/gold_tests/tls_hooks/gold/ts-cert-1.gold
b/tests/gold_tests/tls_hooks/gold/ts-cert-1.gold
index 91f7b38..4a77e23 100644
--- a/tests/gold_tests/tls_hooks/gold/ts-cert-1.gold
+++ b/tests/gold_tests/tls_hooks/gold/ts-cert-1.gold
@@ -1,3 +1,3 @@
-`` DIAG: (ssl_hook_test) Setup callbacks pa=0 sni=0 cert=1 cert_imm=0
pa_delay=0
+`` DIAG: (ssl_hook_test) Setup callbacks pa=0 client_hello=0
client_hello_imm=0 sni=0 cert=1 cert_imm=0 pa_delay=0
`` DIAG: (ssl_hook_test) Cert callback 0 ssl_vc=``
`` DIAG: (ssl_hook_test) Callback reenable ssl_vc=``
diff --git a/tests/gold_tests/tls_hooks/gold/ts-cert-2.gold
b/tests/gold_tests/tls_hooks/gold/ts-cert-2.gold
index 355c5ae..b56bd6d 100644
--- a/tests/gold_tests/tls_hooks/gold/ts-cert-2.gold
+++ b/tests/gold_tests/tls_hooks/gold/ts-cert-2.gold
@@ -1,4 +1,4 @@
-`` DIAG: (ssl_hook_test) Setup callbacks pa=0 sni=0 cert=2 cert_imm=0
pa_delay=0
+`` DIAG: (ssl_hook_test) Setup callbacks pa=0 client_hello=0
client_hello_imm=0 sni=0 cert=2 cert_imm=0 pa_delay=0
`` DIAG: (ssl_hook_test) Cert callback 0 ssl_vc=``
`` DIAG: (ssl_hook_test) Callback reenable ssl_vc=``
`` DIAG: (ssl_hook_test) Cert callback 1 ssl_vc=``
diff --git a/tests/gold_tests/tls_hooks/gold/ts-cert-im-1.gold
b/tests/gold_tests/tls_hooks/gold/ts-cert-im-1.gold
index c571bae..11bb4e7 100644
--- a/tests/gold_tests/tls_hooks/gold/ts-cert-im-1.gold
+++ b/tests/gold_tests/tls_hooks/gold/ts-cert-im-1.gold
@@ -1,2 +1,2 @@
-`` DIAG: (ssl_hook_test) Setup callbacks pa=0 sni=0 cert=0 cert_imm=1
pa_delay=0
+`` DIAG: (ssl_hook_test) Setup callbacks pa=0 client_hello=0
client_hello_imm=0 sni=0 cert=0 cert_imm=1 pa_delay=0
`` DIAG: (ssl_hook_test) Cert callback 0 ssl_vc=``
diff --git a/tests/gold_tests/tls_hooks/gold/ts-client-hello-1.gold
b/tests/gold_tests/tls_hooks/gold/ts-client-hello-1.gold
new file mode 100644
index 0000000..8f444f3
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/gold/ts-client-hello-1.gold
@@ -0,0 +1,3 @@
+`` DIAG: (ssl_hook_test) Setup callbacks pa=0 client_hello=0
client_hello_imm=1 sni=0 cert=0 cert_imm=0 pa_delay=0
+`` DIAG: (ssl_hook_test) Client Hello callback 0 ``
+``
diff --git a/tests/gold_tests/tls_hooks/gold/ts-client-hello-2.gold
b/tests/gold_tests/tls_hooks/gold/ts-client-hello-2.gold
new file mode 100644
index 0000000..4489a55
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/gold/ts-client-hello-2.gold
@@ -0,0 +1,5 @@
+`` DIAG: (ssl_hook_test) Setup callbacks pa=0 client_hello=2
client_hello_imm=0 sni=0 cert=0 cert_imm=0 pa_delay=0
+`` DIAG: (ssl_hook_test) Client Hello callback 0 ssl_vc=``
+`` DIAG: (ssl_hook_test) Callback reenable ssl_vc=``
+`` DIAG: (ssl_hook_test) Client Hello callback 1 ssl_vc=``
+`` DIAG: (ssl_hook_test) Callback reenable ssl_vc=``
diff --git a/tests/gold_tests/tls_hooks/gold/ts-client-hello-delayed-1.gold
b/tests/gold_tests/tls_hooks/gold/ts-client-hello-delayed-1.gold
new file mode 100644
index 0000000..4cea3cc
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/gold/ts-client-hello-delayed-1.gold
@@ -0,0 +1,4 @@
+`` DIAG: (ssl_hook_test) Setup callbacks pa=0 client_hello=1
client_hello_imm=0 sni=0 cert=0 cert_imm=0 pa_delay=0
+`` DIAG: (ssl_hook_test) Client Hello callback 0 ``
+`` DIAG: (ssl_hook_test) Callback reenable ssl_vc=``
+``
diff --git a/tests/gold_tests/tls_hooks/gold/ts-out-delay-start-2.gold
b/tests/gold_tests/tls_hooks/gold/ts-out-delay-start-2.gold
index 21c5d61..89ef1cc 100644
--- a/tests/gold_tests/tls_hooks/gold/ts-out-delay-start-2.gold
+++ b/tests/gold_tests/tls_hooks/gold/ts-out-delay-start-2.gold
@@ -1,4 +1,4 @@
-`` DIAG: (ssl_hook_test) Setup callbacks pa=0 sni=0 cert=0 cert_imm=0
pa_delay=0
+`` DIAG: (ssl_hook_test) Setup callbacks pa=0 client_hello=0
client_hello_imm=0 sni=0 cert=0 cert_imm=0 pa_delay=0
`` DIAG: (ssl_hook_test) Outbound delay start callback 0 ``
`` DIAG: (ssl_hook_test) Callback reenable ssl_vc=``
`` DIAG: (ssl_hook_test) Outbound delay start callback 1 ``
diff --git a/tests/gold_tests/tls_hooks/gold/ts-preaccept-1.gold
b/tests/gold_tests/tls_hooks/gold/ts-preaccept-1.gold
index c8278ea..a2abb9d 100644
--- a/tests/gold_tests/tls_hooks/gold/ts-preaccept-1.gold
+++ b/tests/gold_tests/tls_hooks/gold/ts-preaccept-1.gold
@@ -1,3 +1,3 @@
-`` DIAG: (ssl_hook_test) Setup callbacks pa=1 sni=0 cert=0 cert_imm=0
pa_delay=0
+`` DIAG: (ssl_hook_test) Setup callbacks pa=1 client_hello=0
client_hello_imm=0 sni=0 cert=0 cert_imm=0 pa_delay=0
`` DIAG: (ssl_hook_test) Pre accept callback 0 `` - event is good
``
diff --git a/tests/gold_tests/tls_hooks/gold/ts-preaccept-2.gold
b/tests/gold_tests/tls_hooks/gold/ts-preaccept-2.gold
index cfac682..f70e872 100644
--- a/tests/gold_tests/tls_hooks/gold/ts-preaccept-2.gold
+++ b/tests/gold_tests/tls_hooks/gold/ts-preaccept-2.gold
@@ -1,4 +1,4 @@
-`` DIAG: (ssl_hook_test) Setup callbacks pa=2 sni=0 cert=0 cert_imm=0
pa_delay=0
+`` DIAG: (ssl_hook_test) Setup callbacks pa=2 client_hello=0
client_hello_imm=0 sni=0 cert=0 cert_imm=0 pa_delay=0
`` DIAG: (ssl_hook_test) Pre accept callback 0 `` - event is good
`` DIAG: (ssl_hook_test) Pre accept callback 1 `` - event is good
``
diff --git
a/tests/gold_tests/tls_hooks/gold/ts-preaccept-delayed-1-immdate-2.gold
b/tests/gold_tests/tls_hooks/gold/ts-preaccept-delayed-1-immdate-2.gold
index 16427c9..685ffb4 100644
--- a/tests/gold_tests/tls_hooks/gold/ts-preaccept-delayed-1-immdate-2.gold
+++ b/tests/gold_tests/tls_hooks/gold/ts-preaccept-delayed-1-immdate-2.gold
@@ -1,4 +1,4 @@
-``DIAG: (ssl_hook_test) Setup callbacks pa=2 sni=0 cert=0 cert_imm=0 pa_delay=1
+``DIAG: (ssl_hook_test) Setup callbacks pa=2 client_hello=0 client_hello_imm=0
sni=0 cert=0 cert_imm=0 pa_delay=1
``DIAG: (ssl_hook_test) Pre accept callback 0 `` - event is good
``DIAG: (ssl_hook_test) Pre accept callback 1 `` - event is good
``DIAG: (ssl_hook_test) Pre accept delay callback 0 `` - event is good
diff --git a/tests/gold_tests/tls_hooks/gold/ts-preaccept-delayed-1.gold
b/tests/gold_tests/tls_hooks/gold/ts-preaccept-delayed-1.gold
index 0b85e17..3ebced3 100644
--- a/tests/gold_tests/tls_hooks/gold/ts-preaccept-delayed-1.gold
+++ b/tests/gold_tests/tls_hooks/gold/ts-preaccept-delayed-1.gold
@@ -1,3 +1,3 @@
-`` DIAG: (ssl_hook_test) Setup callbacks pa=0 sni=0 cert=0 cert_imm=0
pa_delay=1
+`` DIAG: (ssl_hook_test) Setup callbacks pa=0 client_hello=0
client_hello_imm=0 sni=0 cert=0 cert_imm=0 pa_delay=1
`` DIAG: (ssl_hook_test) Pre accept delay callback 0 `` - event is good
``
diff --git a/tests/gold_tests/tls_hooks/gold/ts-preaccept1-sni1-cert1.gold
b/tests/gold_tests/tls_hooks/gold/ts-preaccept1-sni1-cert1.gold
index 07ee131..b028f5e 100644
--- a/tests/gold_tests/tls_hooks/gold/ts-preaccept1-sni1-cert1.gold
+++ b/tests/gold_tests/tls_hooks/gold/ts-preaccept1-sni1-cert1.gold
@@ -1,4 +1,4 @@
-`` DIAG: (ssl_hook_test) Setup callbacks pa=1 sni=1 cert=1 cert_imm=0
pa_delay=0
+`` DIAG: (ssl_hook_test) Setup callbacks pa=1 client_hello=0
client_hello_imm=0 sni=1 cert=1 cert_imm=0 pa_delay=0
`` DIAG: (ssl_hook_test) Pre accept callback 0 `` - event is good
`` DIAG: (ssl_hook_test) SNI callback 0 ``
`` DIAG: (ssl_hook_test) Cert callback 0 ssl_vc=``
diff --git a/tests/gold_tests/tls_hooks/gold/ts-sni-1.gold
b/tests/gold_tests/tls_hooks/gold/ts-sni-1.gold
index 4b7d335..bda783a 100644
--- a/tests/gold_tests/tls_hooks/gold/ts-sni-1.gold
+++ b/tests/gold_tests/tls_hooks/gold/ts-sni-1.gold
@@ -1,3 +1,3 @@
-`` DIAG: (ssl_hook_test) Setup callbacks pa=0 sni=1 cert=0 cert_imm=0
pa_delay=0
+`` DIAG: (ssl_hook_test) Setup callbacks pa=0 client_hello=0
client_hello_imm=0 sni=1 cert=0 cert_imm=0 pa_delay=0
`` DIAG: (ssl_hook_test) SNI callback 0 ``
``
diff --git a/tests/gold_tests/tls_hooks/gold/ts-sni-2.gold
b/tests/gold_tests/tls_hooks/gold/ts-sni-2.gold
index ecb2cb6..7ca56f72 100644
--- a/tests/gold_tests/tls_hooks/gold/ts-sni-2.gold
+++ b/tests/gold_tests/tls_hooks/gold/ts-sni-2.gold
@@ -1,4 +1,4 @@
-`` DIAG: (ssl_hook_test) Setup callbacks pa=0 sni=2 cert=0 cert_imm=0
pa_delay=0
+`` DIAG: (ssl_hook_test) Setup callbacks pa=0 client_hello=0
client_hello_imm=0 sni=2 cert=0 cert_imm=0 pa_delay=0
`` DIAG: (ssl_hook_test) SNI callback 0 ``
`` DIAG: (ssl_hook_test) SNI callback 1 ``
``
diff --git a/tests/gold_tests/tls_hooks/tls_hooks16.test.py
b/tests/gold_tests/tls_hooks/tls_hooks16.test.py
new file mode 100644
index 0000000..877c320
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/tls_hooks16.test.py
@@ -0,0 +1,79 @@
+'''
+Test single immediate client hello hook
+'''
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import re
+
+Test.Summary = '''
+Test different combinations of TLS handshake hooks to ensure they are applied
consistently.
+'''
+
+Test.SkipUnless(
+ Condition.HasProgram("grep", "grep needs to be installed on system for
this test to work"),
+ Condition.HasOpenSSLVersion("1.1.1"))
+
+ts = Test.MakeATSProcess("ts", select_ports=False)
+server = Test.MakeOriginServer("server")
+request_header = {"headers": "GET / HTTP/1.1\r\nHost:
www.example.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
+# desired response form the origin server
+response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n",
"timestamp": "1469733493.993", "body": ""}
+server.addResponse("sessionlog.json", request_header, response_header)
+
+ts.addSSLfile("ssl/server.pem")
+ts.addSSLfile("ssl/server.key")
+
+ts.Variables.ssl_port = 4443
+ts.Disk.records_config.update({
+ 'proxy.config.diags.debug.enabled': 1,
+ 'proxy.config.diags.debug.tags': 'ssl_hook_test',
+ 'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir),
+ 'proxy.config.ssl.server.private_key.path':
'{0}'.format(ts.Variables.SSLDir),
+ # enable ssl port
+ 'proxy.config.http.server_ports': '{0}:ssl'.format(ts.Variables.ssl_port),
+ 'proxy.config.ssl.client.verify.server': 0,
+ 'proxy.config.ssl.server.cipher_suite':
'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:RC4-MD5:AES128-SHA:AES256-SHA:DES-CBC3-SHA!SRP:!DSS:!PSK:!aNULL:!eNULL:!SSLv2',
+})
+
+ts.Disk.ssl_multicert_config.AddLine(
+ 'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key'
+)
+
+ts.Disk.remap_config.AddLine(
+ 'map https://example.com:4443
http://127.0.0.1:{0}'.format(server.Variables.Port)
+)
+
+Test.PreparePlugin(os.path.join(Test.Variables.AtsTestToolsDir, 'plugins',
'ssl_hook_test.cc'), ts, '-client_hello_imm=1')
+
+tr = Test.AddTestRun("Test one immediate client hello hook")
+tr.Processes.Default.StartBefore(server)
+tr.Processes.Default.StartBefore(Test.Processes.ts,
ready=When.PortOpen(ts.Variables.ssl_port))
+tr.StillRunningAfter = ts
+tr.StillRunningAfter = server
+tr.Processes.Default.Command = 'curl -k -H \'host:example.com:{0}\'
https://127.0.0.1:{0}'.format(ts.Variables.ssl_port)
+tr.Processes.Default.ReturnCode = 0
+tr.Processes.Default.Streams.stdout = "gold/client-hello-1.gold"
+
+ts.Streams.stderr = "gold/ts-client-hello-1.gold"
+
+snistring = "Client Hello callback 0"
+ts.Streams.All = Testers.ContainsExpression(
+ "\A(?:(?!{0}).)*{0}(?!.*{0}).*\Z".format(snistring), "Client Hello message
appears only once", reflags=re.S | re.M)
+
+tr.Processes.Default.TimeOut = 5
+tr.TimeOut = 5
diff --git a/tests/gold_tests/tls_hooks/tls_hooks17.test.py
b/tests/gold_tests/tls_hooks/tls_hooks17.test.py
new file mode 100644
index 0000000..327c36d
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/tls_hooks17.test.py
@@ -0,0 +1,79 @@
+'''
+Test single delayed client hello hook
+'''
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import re
+
+Test.Summary = '''
+Test different combinations of TLS handshake hooks to ensure they are applied
consistently.
+'''
+
+Test.SkipUnless(
+ Condition.HasProgram("grep", "grep needs to be installed on system for
this test to work"),
+ Condition.HasOpenSSLVersion("1.1.1"))
+
+ts = Test.MakeATSProcess("ts", select_ports=False)
+server = Test.MakeOriginServer("server")
+request_header = {"headers": "GET / HTTP/1.1\r\nHost:
www.example.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
+# desired response form the origin server
+response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n",
"timestamp": "1469733493.993", "body": ""}
+server.addResponse("sessionlog.json", request_header, response_header)
+
+ts.addSSLfile("ssl/server.pem")
+ts.addSSLfile("ssl/server.key")
+
+ts.Variables.ssl_port = 4443
+ts.Disk.records_config.update({
+ 'proxy.config.diags.debug.enabled': 1,
+ 'proxy.config.diags.debug.tags': 'ssl_hook_test',
+ 'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir),
+ 'proxy.config.ssl.server.private_key.path':
'{0}'.format(ts.Variables.SSLDir),
+ # enable ssl port
+ 'proxy.config.http.server_ports': '{0}:ssl'.format(ts.Variables.ssl_port),
+ 'proxy.config.ssl.client.verify.server': 0,
+ 'proxy.config.ssl.server.cipher_suite':
'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:RC4-MD5:AES128-SHA:AES256-SHA:DES-CBC3-SHA!SRP:!DSS:!PSK:!aNULL:!eNULL:!SSLv2',
+})
+
+ts.Disk.ssl_multicert_config.AddLine(
+ 'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key'
+)
+
+ts.Disk.remap_config.AddLine(
+ 'map https://example.com:4443
http://127.0.0.1:{0}'.format(server.Variables.Port)
+)
+
+Test.PreparePlugin(os.path.join(Test.Variables.AtsTestToolsDir, 'plugins',
'ssl_hook_test.cc'), ts, '-client_hello=1')
+
+tr = Test.AddTestRun("Test one delayed client hello hook")
+tr.Processes.Default.StartBefore(server)
+tr.Processes.Default.StartBefore(Test.Processes.ts,
ready=When.PortOpen(ts.Variables.ssl_port))
+tr.StillRunningAfter = ts
+tr.StillRunningAfter = server
+tr.Processes.Default.Command = 'curl -k -H \'host:example.com:{0}\'
https://127.0.0.1:{0}'.format(ts.Variables.ssl_port)
+tr.Processes.Default.ReturnCode = 0
+tr.Processes.Default.Streams.stdout = "gold/client-hello-1.gold"
+
+ts.Streams.stderr = "gold/ts-client-hello-delayed-1.gold"
+
+snistring = "Client Hello callback 0"
+ts.Streams.All = Testers.ContainsExpression(
+ "\A(?:(?!{0}).)*{0}(?!.*{0}).*\Z".format(snistring), "Client Hello message
appears only once", reflags=re.S | re.M)
+
+tr.Processes.Default.TimeOut = 5
+tr.TimeOut = 5
diff --git a/tests/gold_tests/tls_hooks/tls_hooks18.test.py
b/tests/gold_tests/tls_hooks/tls_hooks18.test.py
new file mode 100644
index 0000000..4ce45d1
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/tls_hooks18.test.py
@@ -0,0 +1,83 @@
+'''
+Test two delayed client hello callbacks
+'''
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import re
+
+Test.Summary = '''
+Test different combinations of TLS handshake hooks to ensure they are applied
consistently.
+'''
+
+Test.SkipUnless(
+ Condition.HasProgram("grep", "grep needs to be installed on system for
this test to work"),
+ Condition.HasOpenSSLVersion("1.1.1")
+ )
+
+ts = Test.MakeATSProcess("ts", select_ports=False)
+server = Test.MakeOriginServer("server")
+request_header = {"headers": "GET / HTTP/1.1\r\nHost:
www.example.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
+# desired response form the origin server
+response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n",
"timestamp": "1469733493.993", "body": ""}
+server.addResponse("sessionlog.json", request_header, response_header)
+
+ts.addSSLfile("ssl/server.pem")
+ts.addSSLfile("ssl/server.key")
+
+ts.Variables.ssl_port = 4443
+ts.Disk.records_config.update({
+ 'proxy.config.diags.debug.enabled': 1,
+ 'proxy.config.diags.debug.tags': 'ssl_hook_test',
+ 'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir),
+ 'proxy.config.ssl.server.private_key.path':
'{0}'.format(ts.Variables.SSLDir),
+ # enable ssl port
+ 'proxy.config.http.server_ports': '{0}:ssl'.format(ts.Variables.ssl_port),
+ 'proxy.config.ssl.client.verify.server': 0,
+ 'proxy.config.ssl.server.cipher_suite':
'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:RC4-MD5:AES128-SHA:AES256-SHA:DES-CBC3-SHA!SRP:!DSS:!PSK:!aNULL:!eNULL:!SSLv2',
+})
+
+ts.Disk.ssl_multicert_config.AddLine(
+ 'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key'
+)
+
+ts.Disk.remap_config.AddLine(
+ 'map https://example.com:4443
http://127.0.0.1:{0}'.format(server.Variables.Port)
+)
+
+Test.PreparePlugin(os.path.join(Test.Variables.AtsTestToolsDir, 'plugins',
'ssl_hook_test.cc'), ts, '-client_hello=2')
+
+tr = Test.AddTestRun("Test two client hello hooks")
+tr.Processes.Default.StartBefore(server)
+tr.Processes.Default.StartBefore(Test.Processes.ts,
ready=When.PortOpen(ts.Variables.ssl_port))
+tr.StillRunningAfter = ts
+tr.StillRunningAfter = server
+tr.Processes.Default.Command = 'curl -k -H \'host:example.com:{0}\'
https://127.0.0.1:{0}'.format(ts.Variables.ssl_port)
+tr.Processes.Default.ReturnCode = 0
+tr.Processes.Default.Streams.stdout = "gold/preaccept-1.gold"
+
+ts.Streams.stderr = "gold/ts-client-hello-2.gold"
+
+certstring0 = "Client Hello callback 0"
+certstring1 = "Client Hello callback 1"
+ts.Streams.All = Testers.ContainsExpression(
+ "\A(?:(?!{0}).)*{0}(?!.*{0}).*\Z".format(certstring0), "Cert message
appears only once", reflags=re.S | re.M)
+ts.Streams.All = Testers.ContainsExpression(
+ "\A(?:(?!{0}).)*{0}(?!.*{0}).*\Z".format(certstring1), "Cert message
appears only once", reflags=re.S | re.M)
+
+tr.Processes.Default.TimeOut = 5
+tr.TimeOut = 5
diff --git a/tests/tools/plugins/ssl_hook_test.cc
b/tests/tools/plugins/ssl_hook_test.cc
index a863c8c..2c9f02f 100644
--- a/tests/tools/plugins/ssl_hook_test.cc
+++ b/tests/tools/plugins/ssl_hook_test.cc
@@ -141,6 +141,38 @@ CB_out_close(TSCont cont, TSEvent event, void *edata)
TSVConnReenable(ssl_vc);
return TS_SUCCESS;
}
+int
+CB_Client_Hello_Immediate(TSCont cont, TSEvent event, void *edata)
+{
+ TSVConn ssl_vc = reinterpret_cast<TSVConn>(edata);
+
+ int count = reinterpret_cast<intptr_t>(TSContDataGet(cont));
+
+ TSDebug(PN, "Client Hello callback %d ssl_vc=%p", count, ssl_vc);
+
+ // All done, reactivate things
+ TSVConnReenable(ssl_vc);
+ return TS_SUCCESS;
+}
+
+int
+CB_Client_Hello(TSCont cont, TSEvent event, void *edata)
+{
+ TSVConn ssl_vc = reinterpret_cast<TSVConn>(edata);
+
+ int count = reinterpret_cast<intptr_t>(TSContDataGet(cont));
+
+ TSDebug(PN, "Client Hello callback %d ssl_vc=%p", count, ssl_vc);
+
+ TSCont cb = TSContCreate(&ReenableSSL, TSMutexCreate());
+
+ TSContDataSet(cb, ssl_vc);
+
+ // Schedule to reenable in a bit
+ TSContScheduleOnPool(cb, 2000, TS_THREAD_POOL_NET);
+
+ return TS_SUCCESS;
+}
int
CB_SNI(TSCont cont, TSEvent event, void *edata)
@@ -189,9 +221,9 @@ CB_Cert(TSCont cont, TSEvent event, void *edata)
}
void
-parse_callbacks(int argc, const char *argv[], int &preaccept_count, int
&sni_count, int &cert_count, int &cert_count_immediate,
- int &preaccept_count_delay, int &close_count, int
&out_start_count, int &out_start_delay_count,
- int &out_close_count)
+parse_callbacks(int argc, const char *argv[], int &preaccept_count, int
&client_hello_count, int &client_hello_count_immediate,
+ int &sni_count, int &cert_count, int &cert_count_immediate,
int &preaccept_count_delay, int &close_count,
+ int &out_start_count, int &out_start_delay_count, int
&out_close_count)
{
int i = 0;
const char *ptr;
@@ -215,6 +247,10 @@ parse_callbacks(int argc, const char *argv[], int
&preaccept_count, int &sni_cou
if (ptr) {
if (strncmp(argv[i] + 1, "close", strlen("close")) == 0) {
close_count = atoi(ptr + i);
+ } else if (strncmp(argv[i] + 1, "client_hello_imm",
strlen("client_hello_imm")) == 0) {
+ client_hello_count_immediate = atoi(ptr + i);
+ } else if (strncmp(argv[i] + 1, "client_hello",
strlen("client_hello")) == 0) {
+ client_hello_count = atoi(ptr + i);
} else {
cert_count = atoi(ptr + 1);
}
@@ -249,14 +285,15 @@ parse_callbacks(int argc, const char *argv[], int
&preaccept_count, int &sni_cou
}
void
-setup_callbacks(TSHttpTxn txn, int preaccept_count, int sni_count, int
cert_count, int cert_count_immediate,
- int preaccept_count_delay, int close_count, int
out_start_count, int out_start_delay_count, int out_close_count)
+setup_callbacks(TSHttpTxn txn, int preaccept_count, int client_hello_count,
int client_hello_count_immediate, int sni_count,
+ int cert_count, int cert_count_immediate, int
preaccept_count_delay, int close_count, int out_start_count,
+ int out_start_delay_count, int out_close_count)
{
TSCont cb = nullptr; // pre-accept callback continuation
int i;
- TSDebug(PN, "Setup callbacks pa=%d sni=%d cert=%d cert_imm=%d pa_delay=%d",
preaccept_count, sni_count, cert_count,
- cert_count_immediate, preaccept_count_delay);
+ TSDebug(PN, "Setup callbacks pa=%d client_hello=%d client_hello_imm=%d
sni=%d cert=%d cert_imm=%d pa_delay=%d", preaccept_count,
+ client_hello_count, client_hello_count_immediate, sni_count,
cert_count, cert_count_immediate, preaccept_count_delay);
for (i = 0; i < preaccept_count; i++) {
cb = TSContCreate(&CB_Pre_Accept, TSMutexCreate());
TSContDataSet(cb, (void *)(intptr_t)i);
@@ -275,6 +312,24 @@ setup_callbacks(TSHttpTxn txn, int preaccept_count, int
sni_count, int cert_coun
TSHttpHookAdd(TS_VCONN_START_HOOK, cb);
}
}
+ for (i = 0; i < client_hello_count; i++) {
+ cb = TSContCreate(&CB_Client_Hello, TSMutexCreate());
+ TSContDataSet(cb, (void *)(intptr_t)i);
+ if (txn) {
+ TSHttpTxnHookAdd(txn, TS_SSL_CLIENT_HELLO_HOOK, cb);
+ } else {
+ TSHttpHookAdd(TS_SSL_CLIENT_HELLO_HOOK, cb);
+ }
+ }
+ for (i = 0; i < client_hello_count_immediate; i++) {
+ cb = TSContCreate(&CB_Client_Hello_Immediate, TSMutexCreate());
+ TSContDataSet(cb, (void *)(intptr_t)i);
+ if (txn) {
+ TSHttpTxnHookAdd(txn, TS_SSL_CLIENT_HELLO_HOOK, cb);
+ } else {
+ TSHttpHookAdd(TS_SSL_CLIENT_HELLO_HOOK, cb);
+ }
+ }
for (i = 0; i < sni_count; i++) {
cb = TSContCreate(&CB_SNI, TSMutexCreate());
TSContDataSet(cb, (void *)(intptr_t)i);
@@ -355,18 +410,22 @@ TSPluginInit(int argc, const char *argv[])
TSError("[%s] Plugin registration failed", PN);
}
- int preaccept_count = 0;
- int sni_count = 0;
- int cert_count = 0;
- int cert_count_immediate = 0;
- int preaccept_count_delay = 0;
- int close_count = 0;
- int out_start_count = 0;
- int out_start_delay_count = 0;
- int out_close_count = 0;
- parse_callbacks(argc, argv, preaccept_count, sni_count, cert_count,
cert_count_immediate, preaccept_count_delay, close_count,
- out_start_count, out_start_delay_count, out_close_count);
- setup_callbacks(nullptr, preaccept_count, sni_count, cert_count,
cert_count_immediate, preaccept_count_delay, close_count,
- out_start_count, out_start_delay_count, out_close_count);
+ int preaccept_count = 0;
+ int client_hello_count = 0;
+ int client_hello_count_immediate = 0;
+ int sni_count = 0;
+ int cert_count = 0;
+ int cert_count_immediate = 0;
+ int preaccept_count_delay = 0;
+ int close_count = 0;
+ int out_start_count = 0;
+ int out_start_delay_count = 0;
+ int out_close_count = 0;
+ parse_callbacks(argc, argv, preaccept_count, client_hello_count,
client_hello_count_immediate, sni_count, cert_count,
+ cert_count_immediate, preaccept_count_delay, close_count,
out_start_count, out_start_delay_count,
+ out_close_count);
+ setup_callbacks(nullptr, preaccept_count, client_hello_count,
client_hello_count_immediate, sni_count, cert_count,
+ cert_count_immediate, preaccept_count_delay, close_count,
out_start_count, out_start_delay_count,
+ out_close_count);
return;
}
diff --git a/tests/tools/plugins/test_hooks.cc
b/tests/tools/plugins/test_hooks.cc
index f5c3897..67ff9bb 100644
--- a/tests/tools/plugins/test_hooks.cc
+++ b/tests/tools/plugins/test_hooks.cc
@@ -180,7 +180,6 @@ globalContFunc(TSCont, TSEvent event, void *eventData)
TSVConnReenable(vConn);
} break;
-
case TS_EVENT_SSL_CERT:
case TS_EVENT_SSL_SERVERNAME: {
auto vConn = static_cast<TSVConn>(eventData);
@@ -327,7 +326,6 @@ TSPluginInit(int argc, const char *argv[])
TSHttpHookAdd(TS_HTTP_TXN_CLOSE_HOOK, gCont);
TSHttpHookAdd(TS_SSL_CERT_HOOK, gCont);
TSHttpHookAdd(TS_SSL_SERVERNAME_HOOK, gCont);
-
// NOTE: as of January 2019 these two hooks are only triggered for TLS
connections. It seems that, at trafficserver
// startup, spurious data on the TLS TCP port may cause trafficserver to
attempt (and fail) to create a TLS
// connection. If this happens, it will result in TS_VCONN_START_HOOK being
triggered, and then TS_VCONN_CLOSE_HOOK