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 <shinr...@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 *>("shinr...@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; +}