in native/src/sslcontext.c [138:237]
int ssl_callback_ClientHello(SSL *ssl, int *al, void *arg)
{
JavaVM *javavm = tcn_get_java_vm();
JNIEnv *env;
char *servername = NULL;
const unsigned char *pos;
size_t len, remaining;
tcn_ssl_ctxt_t *c = (tcn_ssl_ctxt_t *) arg;
apr_pool_t *subpool = NULL;
(*javavm)->AttachCurrentThread(javavm, (void **)&env, NULL);
// Continue only if the static method exists
if (sni_java_callback == NULL) {
return SSL_CLIENT_HELLO_SUCCESS;
}
/* We can't use SSL_get_servername() at this earliest OpenSSL connection
* stage, and there is no SSL_client_hello_get0_servername() provided as
* of OpenSSL 1.1.1. So the code below, that extracts the SNI from the
* ClientHello's TLS extensions, is taken from some test code in OpenSSL,
* i.e. client_hello_select_server_ctx() in "test/handshake_helper.c".
*/
/*
* The server_name extension was given too much extensibility when it
* was written, so parsing the normal case is a bit complex.
*/
if (!SSL_client_hello_get0_ext(ssl, TLSEXT_TYPE_server_name, &pos,
&remaining)
|| remaining <= 2)
goto give_up;
/* Extract the length of the supplied list of names. */
len = (*(pos++) << 8);
len += *(pos++);
if (len + 2 != remaining)
goto give_up;
remaining = len;
/*
* The list in practice only has a single element, so we only consider
* the first one.
*/
if (remaining <= 3 || *pos++ != TLSEXT_NAMETYPE_host_name)
goto give_up;
remaining--;
/* Now we can finally pull out the byte array with the actual hostname. */
len = (*(pos++) << 8);
len += *(pos++);
if (len + 2 != remaining)
goto give_up;
/* Use the SNI to switch to the relevant vhost, should it differ from
* c->base_server.
*/
if (apr_pool_create(&subpool, c->pool) != APR_SUCCESS) {
goto give_up;
}
servername = apr_pstrmemdup(subpool, (const char *)pos, len);
give_up:
if (servername != NULL) {
jstring hostname;
jlong original_ssl_context, new_ssl_context;
tcn_ssl_ctxt_t *new_c;
hostname = (*env)->NewStringUTF(env, servername);
original_ssl_context = P2J(c);
new_ssl_context = (*env)->CallStaticLongMethod(env,
ssl_context_class,
sni_java_callback,
original_ssl_context,
hostname);
(*env)->DeleteLocalRef(env, hostname);
if (new_ssl_context != 0 && new_ssl_context != original_ssl_context) {
SSL_CTX *ctx;
new_c = J2P(new_ssl_context, tcn_ssl_ctxt_t *);
ctx = SSL_set_SSL_CTX(ssl, new_c->ctx);
/* Copied from httpd (modules/ssl/ssl_engine_kernel.c) */
SSL_set_options(ssl, SSL_CTX_get_options(ctx));
SSL_set_min_proto_version(ssl, SSL_CTX_get_min_proto_version(ctx));
SSL_set_max_proto_version(ssl, SSL_CTX_get_max_proto_version(ctx));
if ((SSL_get_verify_mode(ssl) == SSL_VERIFY_NONE) ||
(SSL_num_renegotiations(ssl) == 0)) {
SSL_set_verify(ssl, SSL_CTX_get_verify_mode(ctx), SSL_CTX_get_verify_callback(ctx));
}
if (SSL_num_renegotiations(ssl) == 0) {
SSL_set_session_id_context(ssl, &(c->context_id[0]), sizeof c->context_id);
}
}
}
if (subpool != NULL) {
apr_pool_destroy(subpool);
}
return SSL_CLIENT_HELLO_SUCCESS;
}