You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by sh...@apache.org on 2019/05/08 20:30:32 UTC
[trafficserver] branch master updated: Add API and fix logic for
TS_SSL_VERIFY_*_HOOK.
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 f2519d1 Add API and fix logic for TS_SSL_VERIFY_*_HOOK.
f2519d1 is described below
commit f2519d1e323c420d2460781f4c45ad9cac6e7dc8
Author: Susan Hinrichs <sh...@oath.com>
AuthorDate: Wed May 1 19:54:40 2019 +0000
Add API and fix logic for TS_SSL_VERIFY_*_HOOK.
---
.../api/functions/TSVConnSslVerifyCTXGet.en.rst | 51 ++++++
.../hooks-and-transactions/ssl-hooks.en.rst | 18 +-
include/ts/apidefs.h.in | 1 +
include/ts/ts.h | 2 +
iocore/net/P_SSLNetVConnection.h | 16 ++
iocore/net/SSLClientUtils.cc | 2 +
iocore/net/SSLNetVConnection.cc | 2 +-
iocore/net/SSLUtils.cc | 10 +-
src/traffic_server/InkAPI.cc | 11 ++
.../gold_tests/tls/tls_hooks_client_verify.test.py | 112 +++++++++++++
tests/tools/plugins/ssl_client_verify_test.cc | 183 +++++++++++++++++++++
11 files changed, 399 insertions(+), 9 deletions(-)
diff --git a/doc/developer-guide/api/functions/TSVConnSslVerifyCTXGet.en.rst b/doc/developer-guide/api/functions/TSVConnSslVerifyCTXGet.en.rst
new file mode 100644
index 0000000..f2dd6ea
--- /dev/null
+++ b/doc/developer-guide/api/functions/TSVConnSslVerifyCTXGet.en.rst
@@ -0,0 +1,51 @@
+.. 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.
+
+.. include:: ../../../common.defs
+
+.. default-domain:: c
+
+TSVConnSslVerifyCTXGet
+***********************
+
+Synopsis
+========
+
+`#include <ts/ts.h>`
+
+.. function:: TSSslVerifyCTX TSVConnSslVerifyCTXGet(TSVConn svc)
+
+Description
+===========
+
+Get the TSSslVerifyCTX object that corresponds to the certificates being verified for the SSL connection
+corresponding to :arg:`svc`.
+
+This value is only meaningful during the peer certificate verification callbacks, specifically during callbacks
+invoked from the TS_SSL_VERIFY_SERVER_HOOK and TS_SSL_VERIFY_CLIENT_HOOK.
+
+Types
+=====
+
+.. type:: TSSslConnection
+
+ The SSL (per connection) object. This is an opaque type that can be cast to the
+ appropriate type (:code:`SSL *` for the OpenSSL library).
+
+.. type:: TSSslVerifyCTX
+
+ The SSL object that corresponds to the peer certificates being verified. This is an
+ opaque type that can be cast to the appropriate implementation type (:code `X509_STORE_CTX *` for the OpenSSL library).
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 1b7d49c..d164cb2 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
@@ -95,23 +95,27 @@ TS_SSL_VERIFY_CLIENT_HOOK
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.
+use the TSVConn argument and fetch the TSSslVerifyCTX object using the :c:func:`TXVConnSslVerifyCTXGet()`
+method and fetch the peer's certificates to make any additional checks.
Processing will continue regardless of whether the hook callback executes
:c:func:`TSVConnReenable()` since the openssl implementation does not allow
-for pausing processing during the certificate verify callback.
+for pausing processing during the certificate verify callback. The plugin can
+use the :c:func:`TSConnReenableEx()` function to pass in the TS_EVENT_ERROR and
+stop the TLS handshake.
TS_SSL_VERIFY_SERVER_HOOK
-------------------------
-This hooks is called when a Traffic Server connects to an origin and the origin
-presents a certificate. The callback can get the SSL object from the TSVConn
-argument and use that to access the origin certificate and make any additional checks.
+This hook is called when a Traffic Server connects to an origin and the origin
+presents a certificate. The callback can use the TSVConn argument and fetch the
+TSSslVerifyCTX object using the :c:func:`TXVConnSslVerifyCTXGet()`
+method and fetch the peer's certificates to make any additional checks.
Processing will continue regardless of whether the hook callback executes
:c:func:`TSVConnReenable()` since the openssl implementation does not allow
-for pausing processing during the certificate verify callback.
+for pausing processing during the certificate verify callback. The plugin can use
+the :c:func:`TSConnReenableEx()` function to pass in the TS_EVENT_ERROR and
TS_VCONN_OUTBOUND_START_HOOK
----------------------------
diff --git a/include/ts/apidefs.h.in b/include/ts/apidefs.h.in
index 4841d56..cc34840 100644
--- a/include/ts/apidefs.h.in
+++ b/include/ts/apidefs.h.in
@@ -898,6 +898,7 @@ typedef struct tsapi_httpparser *TSHttpParser;
typedef struct tsapi_cachekey *TSCacheKey;
typedef struct tsapi_cachehttpinfo *TSCacheHttpInfo;
typedef struct tsapi_cachetxn *TSCacheTxn;
+typedef struct tsapi_x509_store_ctx *TSSslVerifyCTX;
typedef struct tsapi_port *TSPortDescriptor;
typedef struct tsapi_vio *TSVIO;
diff --git a/include/ts/ts.h b/include/ts/ts.h
index cd4f46a..6552969 100644
--- a/include/ts/ts.h
+++ b/include/ts/ts.h
@@ -1230,6 +1230,8 @@ tsapi void TSVConnReenableEx(TSVConn sslvcp, TSEvent event);
tsapi TSReturnCode TSVConnTunnel(TSVConn sslp);
/* Return the SSL object associated with the connection */
tsapi TSSslConnection TSVConnSSLConnectionGet(TSVConn sslp);
+/* Return the intermediate X509StoreCTX object that references the certificate being validated */
+tsapi TSSslVerifyCTX TSVConnSslVerifyCTXGet(TSVConn sslp);
/* Fetch a SSL context from the global lookup table */
tsapi TSSslContext TSSslContextFindByName(const char *name);
tsapi TSSslContext TSSslContextFindByAddr(struct sockaddr const *);
diff --git a/iocore/net/P_SSLNetVConnection.h b/iocore/net/P_SSLNetVConnection.h
index 7f35010..23733a2 100644
--- a/iocore/net/P_SSLNetVConnection.h
+++ b/iocore/net/P_SSLNetVConnection.h
@@ -383,6 +383,21 @@ public:
bool protocol_mask_set = false;
unsigned long protocol_mask;
+ // 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;
+ }
+
private:
std::string_view map_tls_protocol_to_tag(const char *proto_string) const;
bool update_rbio(bool move_to_socket);
@@ -425,6 +440,7 @@ private:
char *tunnel_host = nullptr;
in_port_t tunnel_port = 0;
bool tunnel_decrypt = false;
+ X509_STORE_CTX *verify_cert = nullptr;
};
typedef int (SSLNetVConnection::*SSLNetVConnHandler)(int, void *);
diff --git a/iocore/net/SSLClientUtils.cc b/iocore/net/SSLClientUtils.cc
index 41aa534..b60225c 100644
--- a/iocore/net/SSLClientUtils.cc
+++ b/iocore/net/SSLClientUtils.cc
@@ -117,7 +117,9 @@ verify_callback(int signature_ok, X509_STORE_CTX *ctx)
}
}
// If the previous configured checks passed, give the hook a try
+ netvc->set_verify_cert(ctx);
netvc->callHooks(TS_EVENT_SSL_VERIFY_SERVER);
+ netvc->set_verify_cert(nullptr);
if (netvc->getSSLHandShakeComplete()) { // hook moved the handshake state to terminal
unsigned char *sni_name;
char buff[INET6_ADDRSTRLEN];
diff --git a/iocore/net/SSLNetVConnection.cc b/iocore/net/SSLNetVConnection.cc
index e4c9fbf..af450ed 100644
--- a/iocore/net/SSLNetVConnection.cc
+++ b/iocore/net/SSLNetVConnection.cc
@@ -1021,7 +1021,6 @@ SSLNetVConnection::sslStartHandShake(int event, int &err)
SSLErrorVC(this, "failed to create SSL server session");
return EVENT_ERROR;
}
-
return sslServerHandShakeEvent(err);
case SSL_EVENT_CLIENT:
@@ -1540,6 +1539,7 @@ SSLNetVConnection::reenable(NetHandler *nh, int event)
sslHandshakeHookState = HANDSHAKE_HOOKS_CERT;
break;
case HANDSHAKE_HOOKS_VERIFY_SERVER:
+ case HANDSHAKE_HOOKS_CLIENT_CERT:
if (event == TS_EVENT_ERROR) {
sslHandshakeStatus = SSL_HANDSHAKE_ERROR;
}
diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc
index efc3f3e..ca05cd9 100644
--- a/iocore/net/SSLUtils.cc
+++ b/iocore/net/SSLUtils.cc
@@ -352,7 +352,15 @@ ssl_verify_client_callback(int preverify_ok, X509_STORE_CTX *ctx)
auto *ssl = static_cast<SSL *>(X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
SSLNetVConnection *netvc = SSLNetVCAccess(ssl);
+ netvc->set_verify_cert(ctx);
netvc->callHooks(TS_EVENT_SSL_VERIFY_CLIENT);
+ netvc->set_verify_cert(nullptr);
+
+ if (netvc->getSSLHandShakeComplete()) { // 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;
+ }
+
return preverify_ok;
}
@@ -1164,7 +1172,7 @@ setClientCertLevel(SSL *ssl, uint8_t certLevel)
}
Debug("ssl", "setting cert level to %d", server_verify_client);
- SSL_set_verify(ssl, server_verify_client, nullptr);
+ SSL_set_verify(ssl, server_verify_client, ssl_verify_client_callback);
SSL_set_verify_depth(ssl, params->verify_depth); // might want to make configurable at some point.
}
diff --git a/src/traffic_server/InkAPI.cc b/src/traffic_server/InkAPI.cc
index f2a2bd5..0cd9c7d 100644
--- a/src/traffic_server/InkAPI.cc
+++ b/src/traffic_server/InkAPI.cc
@@ -8962,6 +8962,17 @@ TSVConnSSLConnectionGet(TSVConn sslp)
return ssl;
}
+tsapi 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());
+ }
+ return nullptr;
+}
+
tsapi TSSslContext
TSSslContextFindByName(const char *name)
{
diff --git a/tests/gold_tests/tls/tls_hooks_client_verify.test.py b/tests/gold_tests/tls/tls_hooks_client_verify.test.py
new file mode 100644
index 0000000..5978ad2
--- /dev/null
+++ b/tests/gold_tests/tls/tls_hooks_client_verify.test.py
@@ -0,0 +1,112 @@
+'''
+Test SERVER_VERIFY_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"))
+
+ts = Test.MakeATSProcess("ts", select_ports=False)
+server = Test.MakeOriginServer("server", ssl=True)
+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.addSSLfile("ssl/signer.pem")
+
+ts.Variables.ssl_port = 4443
+ts.Disk.records_config.update({
+ # Test looks for debug output from the plugin
+ 'proxy.config.diags.debug.enabled': 1,
+ 'proxy.config.diags.debug.tags': 'ssl_client_verify_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.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',
+ 'proxy.config.exec_thread.autoconfig.scale': 1.0,
+ 'proxy.config.ssl.CA.cert.filename': '{0}/signer.pem'.format(ts.Variables.SSLDir),
+ 'proxy.config.url_remap.pristine_host_hdr': 1
+})
+
+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://foo.com:{1}/ https://127.0.0.1:{0}'.format(server.Variables.SSL_Port, ts.Variables.ssl_port)
+)
+ts.Disk.remap_config.AddLine(
+ 'map https://bar.com:{1}/ https://127.0.0.1:{0}'.format(server.Variables.SSL_Port, ts.Variables.ssl_port)
+)
+ts.Disk.remap_config.AddLine(
+ 'map https://random.com:{1}/ https://127.0.0.1:{0}'.format(server.Variables.SSL_Port, ts.Variables.ssl_port)
+)
+
+ts.Disk.ssl_server_name_yaml.AddLines([
+ '- fqdn: bar.com',
+ ' verify_client: STRICT',
+ '- fqdn: foo.com',
+ ' verify_client: STRICT',
+])
+
+Test.PreparePlugin(os.path.join(Test.Variables.AtsTestToolsDir, 'plugins', 'ssl_client_verify_test.cc'), ts, '-count=2 -good=foo.com')
+
+tr = Test.AddTestRun("request good name")
+tr.Setup.Copy("ssl/signed-foo.pem")
+tr.Setup.Copy("ssl/signed-foo.key")
+tr.Setup.Copy("ssl/signed-bar.pem")
+tr.Setup.Copy("ssl/signed-bar.key")
+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 --tls-max 1.2 -k --cert ./signed-foo.pem --key ./signed-foo.key --resolve 'foo.com:{0}:127.0.0.1' https://foo.com:{0}/case1".format(ts.Variables.ssl_port)
+tr.Processes.Default.ReturnCode = 0
+tr.Processes.Default.Streams.all = Testers.ExcludesExpression("Could Not Connect", "Curl attempt should have succeeded")
+
+
+tr2 = Test.AddTestRun("request bad name")
+tr2.StillRunningAfter = ts
+tr2.StillRunningAfter = server
+tr2.Processes.Default.Command = "curl --tls-max 1.2 -k --cert ./signed-bar.pem --key ./signed-bar.key --resolve 'foo.com:{0}:127.0.0.1' https://foo.com:{0}/case1".format(ts.Variables.ssl_port)
+tr2.Processes.Default.ReturnCode = 35
+tr2.Processes.Default.Streams.all = Testers.ContainsExpression("error", "Curl attempt should have failed")
+
+tr3 = Test.AddTestRun("request badly signed cert")
+tr3.Setup.Copy("ssl/server.pem")
+tr3.Setup.Copy("ssl/server.key")
+tr3.StillRunningAfter = ts
+tr3.StillRunningAfter = server
+tr3.Processes.Default.Command = "curl --tls-max 1.2 -k --cert ./server.pem --key ./server.key --resolve 'foo.com:{0}:127.0.0.1' https://foo.com:{0}/case1".format(ts.Variables.ssl_port)
+tr3.Processes.Default.ReturnCode = 35
+tr3.Processes.Default.Streams.all = Testers.ContainsExpression("error", "Curl attempt should have failed")
+
+ts.Streams.All += Testers.ContainsExpression("Client verify callback 0 [\da-fx]+? - event is good good HS", "verify callback happens 2 times")
+ts.Streams.All += Testers.ContainsExpression("Client verify callback 1 [\da-fx]+? - event is good good HS", "verify callback happens 2 times")
+ts.Streams.All += Testers.ContainsExpression("Client verify callback 0 [\da-fx]+? - event is good error HS", "verify callback happens 2 times")
+ts.Streams.All += Testers.ContainsExpression("Client verify callback 1 [\da-fx]+? - event is good error HS", "verify callback happens 2 times")
+
diff --git a/tests/tools/plugins/ssl_client_verify_test.cc b/tests/tools/plugins/ssl_client_verify_test.cc
new file mode 100644
index 0000000..c6967f7
--- /dev/null
+++ b/tests/tools/plugins/ssl_client_verify_test.cc
@@ -0,0 +1,183 @@
+/** @file
+
+ SSL client certificate verification plugin
+ Checks for specificate names in the client provided certificate and
+ fails the handshake if none of the good names are present
+
+ @section license License
+
+ 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.
+ */
+
+#include <ts/ts.h>
+#include <ts/remap.h>
+#include <getopt.h>
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/asn1.h>
+#include <strings.h>
+#include <string.h>
+#include <string>
+#include <map>
+
+#define PN "ssl_client_verify_test"
+#define PCP "[" PN " Plugin] "
+
+std::map<std::string, int> good_names;
+
+bool
+check_name(std::string name)
+{
+ auto entry = good_names.find(name);
+ return entry != good_names.end();
+}
+
+bool
+check_names(X509 *cert)
+{
+ bool retval = false;
+
+ // Check the common name
+ X509_NAME *subject = X509_get_subject_name(cert);
+ if (subject) {
+ int pos = -1;
+ for (; !retval;) {
+ pos = X509_NAME_get_index_by_NID(subject, NID_commonName, pos);
+ if (pos == -1) {
+ break;
+ }
+
+ X509_NAME_ENTRY *e = X509_NAME_get_entry(subject, pos);
+ ASN1_STRING *cn = X509_NAME_ENTRY_get_data(e);
+ char *subj_name = strndup(reinterpret_cast<const char *>(ASN1_STRING_get0_data(cn)), ASN1_STRING_length(cn));
+ retval = check_name(subj_name);
+ free(subj_name);
+ }
+ }
+ if (!retval) {
+ // Check the subjectAltNanes (if present)
+ GENERAL_NAMES *names = (GENERAL_NAMES *)X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr);
+ if (names) {
+ unsigned count = sk_GENERAL_NAME_num(names);
+ for (unsigned i = 0; i < count && !retval; ++i) {
+ GENERAL_NAME *name;
+
+ name = sk_GENERAL_NAME_value(names, i);
+ if (name->type == GEN_DNS) {
+ char *dns =
+ strndup(reinterpret_cast<const char *>(ASN1_STRING_get0_data(name->d.dNSName)), ASN1_STRING_length(name->d.dNSName));
+ retval = check_name(dns);
+ free(dns);
+ }
+ }
+ GENERAL_NAMES_free(names);
+ }
+ }
+ return retval;
+}
+
+int
+CB_client_verify(TSCont cont, TSEvent event, void *edata)
+{
+ TSVConn ssl_vc = reinterpret_cast<TSVConn>(edata);
+
+ int count = reinterpret_cast<intptr_t>(TSContDataGet(cont));
+
+ // Is this a good name or not?
+ TSEvent reenable_event = TS_EVENT_CONTINUE;
+ X509_STORE_CTX *ctx = reinterpret_cast<X509_STORE_CTX *>(TSVConnSslVerifyCTXGet(ssl_vc));
+ if (ctx) {
+ STACK_OF(X509) *chain = X509_STORE_CTX_get1_chain(ctx);
+ // X509 *cert = X509_STORE_CTX_get_current_cert(ctx);
+ bool retval = false;
+ for (int i = 0; i < sk_X509_num(chain) && !retval; i++) {
+ auto cert = sk_X509_value(chain, i);
+ retval = check_names(cert);
+ }
+ if (!retval) {
+ reenable_event = TS_EVENT_ERROR;
+ }
+ } else {
+ reenable_event = TS_EVENT_ERROR;
+ }
+
+ TSDebug(PN, "Client verify callback %d %p - event is %s %s", count, ssl_vc, event == TS_EVENT_SSL_VERIFY_CLIENT ? "good" : "bad",
+ reenable_event == TS_EVENT_ERROR ? "error HS" : "good HS");
+
+ // All done, reactivate things
+ TSVConnReenableEx(ssl_vc, reenable_event);
+ return TS_SUCCESS;
+}
+
+void
+parse_callbacks(int argc, const char *argv[], int &count)
+{
+ int i = 0;
+ const char *ptr;
+ for (i = 0; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ switch (argv[i][1]) {
+ case 'c':
+ ptr = index(argv[i], '=');
+ if (ptr) {
+ count = atoi(ptr + 1);
+ }
+ break;
+ case 'g':
+ ptr = index(argv[i], '=');
+ if (ptr) {
+ good_names.insert(std::pair<std::string, int>(std::string(ptr + 1), 1));
+ }
+ break;
+ }
+ }
+ }
+}
+
+void
+setup_callbacks(int count)
+{
+ TSCont cb = nullptr;
+ int i;
+
+ TSDebug(PN, "Setup callbacks count=%d", count);
+ for (i = 0; i < count; i++) {
+ cb = TSContCreate(&CB_client_verify, TSMutexCreate());
+ TSContDataSet(cb, (void *)(intptr_t)i);
+ TSHttpHookAdd(TS_SSL_VERIFY_CLIENT_HOOK, cb);
+ }
+ return;
+}
+
+// Called by ATS as our initialization point
+void
+TSPluginInit(int argc, const char *argv[])
+{
+ TSPluginRegistrationInfo info;
+ info.plugin_name = const_cast<char *>("SSL verify server test");
+ info.vendor_name = const_cast<char *>("apache");
+ info.support_email = const_cast<char *>("shinrich@apache.org");
+ if (TSPluginRegister(&info) != TS_SUCCESS) {
+ TSError("[%s] Plugin registration failed", PN);
+ }
+
+ int verify_count = 0;
+ parse_callbacks(argc, argv, verify_count);
+ setup_callbacks(verify_count);
+ return;
+}