int ssl_callback_ClientHello()

in native/src/sslcontext.c [137:236]


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;
}