/* 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. */ /** SSL Context wrapper */ #include "tcn.h" #include "apr_file_io.h" #include "apr_thread_mutex.h" #include "apr_poll.h" #ifdef HAVE_OPENSSL #include "ssl_private.h" static jclass byteArrayClass; static apr_status_t ssl_context_cleanup(void *data) { tcn_ssl_ctxt_t *c = (tcn_ssl_ctxt_t *)data; if (c) { int i; if (c->crl) X509_STORE_free(c->crl); c->crl = NULL; if (c->ctx) SSL_CTX_free(c->ctx); c->ctx = NULL; for (i = 0; i < SSL_AIDX_MAX; i++) { if (c->certs[i]) { X509_free(c->certs[i]); c->certs[i] = NULL; } if (c->keys[i]) { EVP_PKEY_free(c->keys[i]); c->keys[i] = NULL; } } if (c->bio_is) { SSL_BIO_close(c->bio_is); c->bio_is = NULL; } if (c->bio_os) { SSL_BIO_close(c->bio_os); c->bio_os = NULL; } if (c->verifier) { JNIEnv *e; tcn_get_java_env(&e); (*e)->DeleteGlobalRef(e, c->verifier); c->verifier = NULL; } c->verifier_method = NULL; if (c->next_proto_data) { free(c->next_proto_data); c->next_proto_data = NULL; } c->next_proto_len = 0; if (c->alpn_proto_data) { free(c->alpn_proto_data); c->alpn_proto_data = NULL; } c->alpn_proto_len = 0; } return APR_SUCCESS; } static jclass ssl_context_class; static jmethodID sni_java_callback; /* Callback used when OpenSSL receives a client hello with a Server Name * Indication extension. */ int ssl_callback_ServerNameIndication(SSL *ssl, int *al, tcn_ssl_ctxt_t *c) { /* TODO: Is it better to cache the JNIEnv* during the call to handshake? */ /* Get the JNI environment for this callback */ JavaVM *javavm = tcn_get_java_vm(); JNIEnv *env; const char *servername; jstring hostname; jlong original_ssl_context, new_ssl_context; tcn_ssl_ctxt_t *new_c; // Continue only if the static method exists if (sni_java_callback == NULL) { return SSL_TLSEXT_ERR_OK; } (*javavm)->AttachCurrentThread(javavm, (void **)&env, NULL); // Get the host name presented by the client servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); // Convert to Java compatible parameters ready for the method call 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); // Delete the local reference as this method is called via callback. // Otherwise local references are only freed once jni method returns. (*env)->DeleteLocalRef(env, hostname); if (new_ssl_context != 0 && new_ssl_context != original_ssl_context) { new_c = J2P(new_ssl_context, tcn_ssl_ctxt_t *); SSL_set_SSL_CTX(ssl, new_c->ctx); } return SSL_TLSEXT_ERR_OK; } /* Initialize server context */ TCN_IMPLEMENT_CALL(jlong, SSLContext, make)(TCN_STDARGS, jlong pool, jint protocol, jint mode) { apr_pool_t *p = J2P(pool, apr_pool_t *); tcn_ssl_ctxt_t *c = NULL; SSL_CTX *ctx = NULL; jclass clazz; #if OPENSSL_VERSION_NUMBER >= 0x10100000L jint prot; #endif UNREFERENCED(o); if (protocol == SSL_PROTOCOL_NONE) { tcn_Throw(e, "No SSL protocols requested"); goto init_failed; } #if OPENSSL_VERSION_NUMBER < 0x10100000L if (protocol == SSL_PROTOCOL_TLSV1_2) { #ifdef HAVE_TLSV1_2 if (mode == SSL_MODE_CLIENT) ctx = SSL_CTX_new(TLSv1_2_client_method()); else if (mode == SSL_MODE_SERVER) ctx = SSL_CTX_new(TLSv1_2_server_method()); else ctx = SSL_CTX_new(TLSv1_2_method()); #endif } else if (protocol == SSL_PROTOCOL_TLSV1_1) { #ifdef HAVE_TLSV1_1 if (mode == SSL_MODE_CLIENT) ctx = SSL_CTX_new(TLSv1_1_client_method()); else if (mode == SSL_MODE_SERVER) ctx = SSL_CTX_new(TLSv1_1_server_method()); else ctx = SSL_CTX_new(TLSv1_1_method()); #endif } else if (protocol == SSL_PROTOCOL_TLSV1) { if (mode == SSL_MODE_CLIENT) ctx = SSL_CTX_new(TLSv1_client_method()); else if (mode == SSL_MODE_SERVER) ctx = SSL_CTX_new(TLSv1_server_method()); else ctx = SSL_CTX_new(TLSv1_method()); } else if (protocol == SSL_PROTOCOL_SSLV3) { if (mode == SSL_MODE_CLIENT) ctx = SSL_CTX_new(SSLv3_client_method()); else if (mode == SSL_MODE_SERVER) ctx = SSL_CTX_new(SSLv3_server_method()); else ctx = SSL_CTX_new(SSLv3_method()); } else if (protocol == SSL_PROTOCOL_SSLV2) { /* requested but not supported */ #ifndef HAVE_TLSV1_2 } else if (protocol & SSL_PROTOCOL_TLSV1_2) { /* requested but not supported */ #endif #ifndef HAVE_TLSV1_1 } else if (protocol & SSL_PROTOCOL_TLSV1_1) { /* requested but not supported */ #endif } else { #endif /* if OPENSSL_VERSION_NUMBER < 0x10100000L */ if (mode == SSL_MODE_CLIENT) ctx = SSL_CTX_new(TLS_client_method()); else if (mode == SSL_MODE_SERVER) ctx = SSL_CTX_new(TLS_server_method()); else ctx = SSL_CTX_new(TLS_method()); #if OPENSSL_VERSION_NUMBER < 0x10100000L } #endif if (!ctx) { char err[256]; ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Invalid Server SSL Protocol (%s)", err); goto init_failed; } if ((c = apr_pcalloc(p, sizeof(tcn_ssl_ctxt_t))) == NULL) { tcn_ThrowAPRException(e, apr_get_os_error()); goto init_failed; } c->protocol = protocol; c->mode = mode; c->ctx = ctx; c->pool = p; c->bio_os = BIO_new(BIO_s_file()); if (c->bio_os != NULL) BIO_set_fp(c->bio_os, stderr, BIO_NOCLOSE | BIO_FP_TEXT); SSL_CTX_set_options(c->ctx, SSL_OP_ALL); #if OPENSSL_VERSION_NUMBER < 0x10100000L /* always disable SSLv2, as per RFC 6176 */ SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2); if (!(protocol & SSL_PROTOCOL_SSLV3)) SSL_CTX_set_options(c->ctx, SSL_OP_NO_SSLv3); if (!(protocol & SSL_PROTOCOL_TLSV1)) SSL_CTX_set_options(c->ctx, SSL_OP_NO_TLSv1); #ifdef HAVE_TLSV1_1 if (!(protocol & SSL_PROTOCOL_TLSV1_1)) SSL_CTX_set_options(c->ctx, SSL_OP_NO_TLSv1_1); #endif #ifdef HAVE_TLSV1_2 if (!(protocol & SSL_PROTOCOL_TLSV1_2)) SSL_CTX_set_options(c->ctx, SSL_OP_NO_TLSv1_2); #endif #else /* if OPENSSL_VERSION_NUMBER < 0x10100000L */ /* We first determine the maximum protocol version we should provide */ if (protocol & SSL_PROTOCOL_TLSV1_2) { prot = TLS1_2_VERSION; } else if (protocol & SSL_PROTOCOL_TLSV1_1) { prot = TLS1_1_VERSION; } else if (protocol & SSL_PROTOCOL_TLSV1) { prot = TLS1_VERSION; } else if (protocol & SSL_PROTOCOL_SSLV3) { prot = SSL3_VERSION; } else { SSL_CTX_free(ctx); tcn_Throw(e, "Invalid Server SSL Protocol (%d)", protocol); goto init_failed; } SSL_CTX_set_max_proto_version(ctx, prot); /* Next we scan for the minimal protocol version we should provide, * but we do not allow holes between max and min */ if (prot == TLS1_2_VERSION && protocol & SSL_PROTOCOL_TLSV1_1) { prot = TLS1_1_VERSION; } if (prot == TLS1_1_VERSION && protocol & SSL_PROTOCOL_TLSV1) { prot = TLS1_VERSION; } if (prot == TLS1_VERSION && protocol & SSL_PROTOCOL_SSLV3) { prot = SSL3_VERSION; } SSL_CTX_set_min_proto_version(ctx, prot); #endif /* if OPENSSL_VERSION_NUMBER < 0x10100000L */ /* * Configure additional context ingredients */ SSL_CTX_set_options(c->ctx, SSL_OP_SINGLE_DH_USE); #ifdef HAVE_ECC SSL_CTX_set_options(c->ctx, SSL_OP_SINGLE_ECDH_USE); #endif #ifdef SSL_OP_NO_COMPRESSION /* Disable SSL compression to be safe */ SSL_CTX_set_options(c->ctx, SSL_OP_NO_COMPRESSION); #endif /** To get back the tomcat wrapper from CTX */ SSL_CTX_set_app_data(c->ctx, (char *)c); #ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION /* * Disallow a session from being resumed during a renegotiation, * so that an acceptable cipher suite can be negotiated. */ SSL_CTX_set_options(c->ctx, SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); #endif #ifdef SSL_MODE_RELEASE_BUFFERS /* Release idle buffers to the SSL_CTX free list */ SSL_CTX_set_mode(c->ctx, SSL_MODE_RELEASE_BUFFERS); #endif /* Default session context id and cache size */ SSL_CTX_sess_set_cache_size(c->ctx, SSL_DEFAULT_CACHE_SIZE); /* Session cache is disabled by default */ SSL_CTX_set_session_cache_mode(c->ctx, SSL_SESS_CACHE_OFF); /* Longer session timeout */ SSL_CTX_set_timeout(c->ctx, 14400); EVP_Digest((const unsigned char *)SSL_DEFAULT_VHOST_NAME, (unsigned long)((sizeof SSL_DEFAULT_VHOST_NAME) - 1), &(c->context_id[0]), NULL, EVP_sha1(), NULL); /* Set default Certificate verification level * and depth for the Client Authentication */ c->verify_depth = 1; c->verify_mode = SSL_CVERIFY_UNSET; c->shutdown_type = SSL_SHUTDOWN_TYPE_UNSET; /* Set default password callback */ SSL_CTX_set_default_passwd_cb(c->ctx, (pem_password_cb *)SSL_password_callback); SSL_CTX_set_default_passwd_cb_userdata(c->ctx, (void *)(&tcn_password_callback)); SSL_CTX_set_info_callback(c->ctx, SSL_callback_handshake); /* Cache Java side SNI callback if not already cached */ if (ssl_context_class == NULL) { ssl_context_class = (*e)->NewGlobalRef(e, o); sni_java_callback = (*e)->GetStaticMethodID(e, ssl_context_class, "sniCallBack", "(JLjava/lang/String;)J"); /* Older Tomcat versions may not have this static method */ if ( (*e)->ExceptionCheck(e) ) { (*e)->ExceptionClear(e); } } /* Set up OpenSSL call back if SNI is provided by the client */ SSL_CTX_set_tlsext_servername_callback(c->ctx, ssl_callback_ServerNameIndication); SSL_CTX_set_tlsext_servername_arg(c->ctx, c); /* * Let us cleanup the ssl context when the pool is destroyed */ apr_pool_cleanup_register(p, (const void *)c, ssl_context_cleanup, apr_pool_cleanup_null); /* Cache the byte[].class for performance reasons */ clazz = (*e)->FindClass(e, "[B"); byteArrayClass = (jclass) (*e)->NewGlobalRef(e, clazz); return P2J(c); init_failed: return 0; } TCN_IMPLEMENT_CALL(jint, SSLContext, free)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); UNREFERENCED_STDARGS; TCN_ASSERT(ctx != 0); /* Run and destroy the cleanup callback */ return apr_pool_cleanup_run(c->pool, c, ssl_context_cleanup); } TCN_IMPLEMENT_CALL(void, SSLContext, setContextId)(TCN_STDARGS, jlong ctx, jstring id) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); TCN_ALLOC_CSTRING(id); TCN_ASSERT(ctx != 0); UNREFERENCED(o); if (J2S(id)) { EVP_Digest((const unsigned char *)J2S(id), (unsigned long)strlen(J2S(id)), &(c->context_id[0]), NULL, EVP_sha1(), NULL); } TCN_FREE_CSTRING(id); } TCN_IMPLEMENT_CALL(void, SSLContext, setBIO)(TCN_STDARGS, jlong ctx, jlong bio, jint dir) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); BIO *bio_handle = J2P(bio, BIO *); UNREFERENCED_STDARGS; TCN_ASSERT(ctx != 0); if (dir == 0) { if (c->bio_os && c->bio_os != bio_handle) SSL_BIO_close(c->bio_os); c->bio_os = bio_handle; } else if (dir == 1) { if (c->bio_is && c->bio_is != bio_handle) SSL_BIO_close(c->bio_is); c->bio_is = bio_handle; } else return; SSL_BIO_doref(bio_handle); } TCN_IMPLEMENT_CALL(void, SSLContext, setOptions)(TCN_STDARGS, jlong ctx, jint opt) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); UNREFERENCED_STDARGS; TCN_ASSERT(ctx != 0); #ifndef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION /* Clear the flag if not supported */ if (opt & 0x00040000) opt &= ~0x00040000; #endif SSL_CTX_set_options(c->ctx, opt); } TCN_IMPLEMENT_CALL(jint, SSLContext, getOptions)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); UNREFERENCED_STDARGS; TCN_ASSERT(ctx != 0); return SSL_CTX_get_options(c->ctx); } TCN_IMPLEMENT_CALL(void, SSLContext, clearOptions)(TCN_STDARGS, jlong ctx, jint opt) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); UNREFERENCED_STDARGS; TCN_ASSERT(ctx != 0); SSL_CTX_clear_options(c->ctx, opt); } TCN_IMPLEMENT_CALL(void, SSLContext, setQuietShutdown)(TCN_STDARGS, jlong ctx, jboolean mode) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); UNREFERENCED_STDARGS; TCN_ASSERT(ctx != 0); SSL_CTX_set_quiet_shutdown(c->ctx, mode ? 1 : 0); } TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCipherSuite)(TCN_STDARGS, jlong ctx, jstring ciphers) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); TCN_ALLOC_CSTRING(ciphers); jboolean rv = JNI_TRUE; #ifndef HAVE_EXPORT_CIPHERS size_t len; char *buf; #endif UNREFERENCED(o); TCN_ASSERT(ctx != 0); if (!J2S(ciphers)) return JNI_FALSE; #ifndef HAVE_EXPORT_CIPHERS /* * Always disable NULL and export ciphers, * no matter what was given in the config. */ len = strlen(J2S(ciphers)) + strlen(SSL_CIPHERS_ALWAYS_DISABLED) + 1; buf = malloc(len * sizeof(char *)); if (buf == NULL) return JNI_FALSE; memcpy(buf, SSL_CIPHERS_ALWAYS_DISABLED, strlen(SSL_CIPHERS_ALWAYS_DISABLED)); memcpy(buf + strlen(SSL_CIPHERS_ALWAYS_DISABLED), J2S(ciphers), strlen(J2S(ciphers))); buf[len - 1] = '\0'; if (!SSL_CTX_set_cipher_list(c->ctx, buf)) { #else if (!SSL_CTX_set_cipher_list(c->ctx, J2S(ciphers))) { #endif char err[256]; ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Unable to configure permitted SSL ciphers (%s)", err); rv = JNI_FALSE; } #ifndef HAVE_EXPORT_CIPHERS free(buf); #endif TCN_FREE_CSTRING(ciphers); return rv; } TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCARevocation)(TCN_STDARGS, jlong ctx, jstring file, jstring path) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); TCN_ALLOC_CSTRING(file); TCN_ALLOC_CSTRING(path); jboolean rv = JNI_FALSE; X509_LOOKUP *lookup; char err[256]; UNREFERENCED(o); TCN_ASSERT(ctx != 0); if (J2S(file) == NULL && J2S(path) == NULL) return JNI_FALSE; if (!c->crl) { #if 1 // CRL FIX if ((c->crl = SSL_CTX_get_cert_store(c->ctx)) == NULL) goto cleanup; #else if ((c->crl = X509_STORE_new()) == NULL) goto cleanup; #endif } if (J2S(file)) { lookup = X509_STORE_add_lookup(c->crl, X509_LOOKUP_file()); if (lookup == NULL) { ERR_error_string(ERR_get_error(), err); X509_STORE_free(c->crl); c->crl = NULL; tcn_Throw(e, "Lookup failed for file %s (%s)", J2S(file), err); goto cleanup; } X509_LOOKUP_load_file(lookup, J2S(file), X509_FILETYPE_PEM); } if (J2S(path)) { lookup = X509_STORE_add_lookup(c->crl, X509_LOOKUP_hash_dir()); if (lookup == NULL) { ERR_error_string(ERR_get_error(), err); X509_STORE_free(c->crl); c->crl = NULL; tcn_Throw(e, "Lookup failed for path %s (%s)", J2S(file), err); goto cleanup; } X509_LOOKUP_add_dir(lookup, J2S(path), X509_FILETYPE_PEM); } rv = JNI_TRUE; cleanup: TCN_FREE_CSTRING(file); TCN_FREE_CSTRING(path); return rv; } TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCertificateChainFile)(TCN_STDARGS, jlong ctx, jstring file, jboolean skipfirst) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jboolean rv = JNI_FALSE; TCN_ALLOC_CSTRING(file); UNREFERENCED(o); TCN_ASSERT(ctx != 0); if (!J2S(file)) return JNI_FALSE; if (SSL_CTX_use_certificate_chain(c->ctx, J2S(file), skipfirst) > 0) rv = JNI_TRUE; TCN_FREE_CSTRING(file); return rv; } TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCACertificate)(TCN_STDARGS, jlong ctx, jstring file, jstring path) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jboolean rv = JNI_TRUE; TCN_ALLOC_CSTRING(file); TCN_ALLOC_CSTRING(path); UNREFERENCED(o); TCN_ASSERT(ctx != 0); if (file == NULL && path == NULL) return JNI_FALSE; /* * Configure Client Authentication details */ if (!SSL_CTX_load_verify_locations(c->ctx, J2S(file), J2S(path))) { char err[256]; ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Unable to configure locations " "for client authentication (%s)", err); rv = JNI_FALSE; goto cleanup; } c->store = SSL_CTX_get_cert_store(c->ctx); if (c->mode) { STACK_OF(X509_NAME) *ca_certs; c->ca_certs++; ca_certs = SSL_CTX_get_client_CA_list(c->ctx); if (ca_certs == NULL) { SSL_load_client_CA_file(J2S(file)); if (ca_certs != NULL) SSL_CTX_set_client_CA_list(c->ctx, ca_certs); } else { if (!SSL_add_file_cert_subjects_to_stack(ca_certs, J2S(file))) ca_certs = NULL; } if (ca_certs == NULL && c->verify_mode == SSL_CVERIFY_REQUIRE) { /* * Give a warning when no CAs were configured but client authentication * should take place. This cannot work. */ if (c->bio_os) { BIO_printf(c->bio_os, "[WARN] Oops, you want to request client " "authentication, but no CAs are known for " "verification!?"); } else { fprintf(stderr, "[WARN] Oops, you want to request client " "authentication, but no CAs are known for " "verification!?"); } } } cleanup: TCN_FREE_CSTRING(file); TCN_FREE_CSTRING(path); return rv; } TCN_IMPLEMENT_CALL(void, SSLContext, setTmpDH)(TCN_STDARGS, jlong ctx, jstring file) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); BIO *bio = NULL; DH *dh = NULL; TCN_ALLOC_CSTRING(file); UNREFERENCED(o); TCN_ASSERT(ctx != 0); TCN_ASSERT(file); if (!J2S(file)) { tcn_Throw(e, "Error while configuring DH: no dh param file given"); return; } bio = BIO_new_file(J2S(file), "r"); if (!bio) { char err[256]; ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Error while configuring DH using %s: %s", J2S(file), err); TCN_FREE_CSTRING(file); return; } dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); BIO_free(bio); if (!dh) { char err[256]; ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Error while configuring DH: no DH parameter found in %s (%s)", J2S(file), err); TCN_FREE_CSTRING(file); return; } if (1 != SSL_CTX_set_tmp_dh(c->ctx, dh)) { char err[256]; DH_free(dh); ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Error while configuring DH with file %s: %s", J2S(file), err); TCN_FREE_CSTRING(file); return; } DH_free(dh); TCN_FREE_CSTRING(file); } TCN_IMPLEMENT_CALL(void, SSLContext, setTmpECDHByCurveName)(TCN_STDARGS, jlong ctx, jstring curveName) { #ifdef HAVE_ECC tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); int i; EC_KEY *ecdh; TCN_ALLOC_CSTRING(curveName); UNREFERENCED(o); TCN_ASSERT(ctx != 0); TCN_ASSERT(curveName); /* First try to get curve by name */ i = OBJ_sn2nid(J2S(curveName)); if (!i) { tcn_Throw(e, "Can't configure elliptic curve: unknown curve name %s", J2S(curveName)); TCN_FREE_CSTRING(curveName); return; } ecdh = EC_KEY_new_by_curve_name(i); if (!ecdh) { tcn_Throw(e, "Can't configure elliptic curve: unknown curve name %s", J2S(curveName)); TCN_FREE_CSTRING(curveName); return; } /* Setting found curve to context */ if (1 != SSL_CTX_set_tmp_ecdh(c->ctx, ecdh)) { char err[256]; EC_KEY_free(ecdh); ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Error while configuring elliptic curve %s: %s", J2S(curveName), err); TCN_FREE_CSTRING(curveName); return; } EC_KEY_free(ecdh); TCN_FREE_CSTRING(curveName); #else tcn_Throw(e, "Cant't configure elliptic curve: unsupported by this OpenSSL version"); return; #endif } TCN_IMPLEMENT_CALL(void, SSLContext, setShutdownType)(TCN_STDARGS, jlong ctx, jint type) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); UNREFERENCED_STDARGS; TCN_ASSERT(ctx != 0); c->shutdown_type = type; } TCN_IMPLEMENT_CALL(void, SSLContext, setVerify)(TCN_STDARGS, jlong ctx, jint level, jint depth) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); int verify = SSL_VERIFY_NONE; UNREFERENCED(o); TCN_ASSERT(ctx != 0); c->verify_mode = level; if (c->verify_mode == SSL_CVERIFY_UNSET) c->verify_mode = SSL_CVERIFY_NONE; if (depth > 0) c->verify_depth = depth; /* * Configure callbacks for SSL context */ if (c->verify_mode == SSL_CVERIFY_REQUIRE) verify |= SSL_VERIFY_PEER_STRICT; if ((c->verify_mode == SSL_CVERIFY_OPTIONAL) || (c->verify_mode == SSL_CVERIFY_OPTIONAL_NO_CA)) verify |= SSL_VERIFY_PEER; if (!c->store) { if (SSL_CTX_set_default_verify_paths(c->ctx)) { c->store = SSL_CTX_get_cert_store(c->ctx); X509_STORE_set_flags(c->store, 0); } else { /* XXX: See if this is fatal */ } } #if 1 // CRL FIX X509_STORE_set_flags(c->store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); #endif SSL_CTX_set_verify(c->ctx, verify, SSL_callback_SSL_verify); } static EVP_PKEY *load_pem_key(tcn_ssl_ctxt_t *c, const char *file) { BIO *bio = NULL; EVP_PKEY *key = NULL; tcn_pass_cb_t *cb_data = c->cb_data; int i; if ((bio = BIO_new(BIO_s_file())) == NULL) { return NULL; } if (BIO_read_filename(bio, file) <= 0) { BIO_free(bio); return NULL; } if (!cb_data) cb_data = &tcn_password_callback; for (i = 0; i < 3; i++) { key = PEM_read_bio_PrivateKey(bio, NULL, (pem_password_cb *)SSL_password_callback, (void *)cb_data); if (key) break; cb_data->password[0] = '\0'; BIO_ctrl(bio, BIO_CTRL_RESET, 0, NULL); } BIO_free(bio); return key; } static X509 *load_pem_cert(tcn_ssl_ctxt_t *c, const char *file) { BIO *bio = NULL; X509 *cert = NULL; tcn_pass_cb_t *cb_data = c->cb_data; if ((bio = BIO_new(BIO_s_file())) == NULL) { return NULL; } if (BIO_read_filename(bio, file) <= 0) { BIO_free(bio); return NULL; } if (!cb_data) cb_data = &tcn_password_callback; cert = PEM_read_bio_X509_AUX(bio, NULL, (pem_password_cb *)SSL_password_callback, (void *)cb_data); if (cert == NULL && (ERR_GET_REASON(ERR_peek_last_error()) == PEM_R_NO_START_LINE)) { ERR_clear_error(); BIO_ctrl(bio, BIO_CTRL_RESET, 0, NULL); cert = d2i_X509_bio(bio, NULL); } BIO_free(bio); return cert; } static int ssl_load_pkcs12(tcn_ssl_ctxt_t *c, const char *file, EVP_PKEY **pkey, X509 **cert, STACK_OF(X509) **ca) { const char *pass; char buff[PEM_BUFSIZE]; int len, rc = 0; PKCS12 *p12; BIO *in; tcn_pass_cb_t *cb_data = c->cb_data; if ((in = BIO_new(BIO_s_file())) == 0) return 0; if (BIO_read_filename(in, file) <= 0) { BIO_free(in); return 0; } p12 = d2i_PKCS12_bio(in, 0); if (p12 == 0) { /* Error loading PKCS12 file */ goto cleanup; } /* See if an empty password will do */ if (PKCS12_verify_mac(p12, "", 0) || PKCS12_verify_mac(p12, 0, 0)) { pass = ""; } else { if (!cb_data) cb_data = &tcn_password_callback; len = SSL_password_callback(buff, PEM_BUFSIZE, 0, cb_data); if (len < 0) { /* Passpharse callback error */ goto cleanup; } if (!PKCS12_verify_mac(p12, buff, len)) { /* Mac verify error (wrong password?) in PKCS12 file */ goto cleanup; } pass = buff; } rc = PKCS12_parse(p12, pass, pkey, cert, ca); cleanup: if (p12 != 0) PKCS12_free(p12); BIO_free(in); return rc; } TCN_IMPLEMENT_CALL(void, SSLContext, setRandom)(TCN_STDARGS, jlong ctx, jstring file) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); TCN_ALLOC_CSTRING(file); TCN_ASSERT(ctx != 0); UNREFERENCED(o); if (J2S(file)) c->rand_file = apr_pstrdup(c->pool, J2S(file)); TCN_FREE_CSTRING(file); } TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCertificate)(TCN_STDARGS, jlong ctx, jstring cert, jstring key, jstring password, jint idx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jboolean rv = JNI_TRUE; TCN_ALLOC_CSTRING(cert); TCN_ALLOC_CSTRING(key); TCN_ALLOC_CSTRING(password); const char *key_file, *cert_file; const char *p; char err[256]; #ifdef HAVE_ECC EC_GROUP *ecparams; int nid; EC_KEY *eckey = NULL; #endif DH *dhparams; UNREFERENCED(o); TCN_ASSERT(ctx != 0); if (idx < 0 || idx >= SSL_AIDX_MAX) { /* TODO: Throw something */ rv = JNI_FALSE; goto cleanup; } if (J2S(password)) { if (!c->cb_data) c->cb_data = &tcn_password_callback; strncpy(c->cb_data->password, J2S(password), SSL_MAX_PASSWORD_LEN); c->cb_data->password[SSL_MAX_PASSWORD_LEN-1] = '\0'; } key_file = J2S(key); cert_file = J2S(cert); if (!key_file) key_file = cert_file; if (!key_file || !cert_file) { tcn_Throw(e, "No Certificate file specified or invalid file format"); rv = JNI_FALSE; goto cleanup; } if ((p = strrchr(cert_file, '.')) != NULL && strcmp(p, ".pkcs12") == 0) { if (!ssl_load_pkcs12(c, cert_file, &c->keys[idx], &c->certs[idx], 0)) { ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Unable to load certificate %s (%s)", cert_file, err); rv = JNI_FALSE; goto cleanup; } } else { if ((c->keys[idx] = load_pem_key(c, key_file)) == NULL) { ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Unable to load certificate key %s (%s)", key_file, err); rv = JNI_FALSE; goto cleanup; } if ((c->certs[idx] = load_pem_cert(c, cert_file)) == NULL) { ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Unable to load certificate %s (%s)", cert_file, err); rv = JNI_FALSE; goto cleanup; } } if (SSL_CTX_use_certificate(c->ctx, c->certs[idx]) <= 0) { ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Error setting certificate (%s)", err); rv = JNI_FALSE; goto cleanup; } if (SSL_CTX_use_PrivateKey(c->ctx, c->keys[idx]) <= 0) { ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Error setting private key (%s)", err); rv = JNI_FALSE; goto cleanup; } if (SSL_CTX_check_private_key(c->ctx) <= 0) { ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Private key does not match the certificate public key (%s)", err); rv = JNI_FALSE; goto cleanup; } /* * Try to read DH parameters from the (first) SSLCertificateFile */ /* XXX Does this also work for pkcs12 or only for PEM files? * If only for PEM files move above to the PEM handling */ if ((idx == 0) && (dhparams = SSL_dh_GetParamFromFile(cert_file))) { SSL_CTX_set_tmp_dh(c->ctx, dhparams); } #ifdef HAVE_ECC /* * Similarly, try to read the ECDH curve name from SSLCertificateFile... */ /* XXX Does this also work for pkcs12 or only for PEM files? * If only for PEM files move above to the PEM handling */ if ((ecparams = SSL_ec_GetParamFromFile(cert_file)) && (nid = EC_GROUP_get_curve_name(ecparams)) && (eckey = EC_KEY_new_by_curve_name(nid))) { SSL_CTX_set_tmp_ecdh(c->ctx, eckey); } /* * ...otherwise, configure NIST P-256 (required to enable ECDHE) */ else { #if defined(SSL_CTX_set_ecdh_auto) SSL_CTX_set_ecdh_auto(c->ctx, 1); #else eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); SSL_CTX_set_tmp_ecdh(c->ctx, eckey); #endif } EC_KEY_free(eckey); #endif SSL_CTX_set_tmp_dh_callback(c->ctx, SSL_callback_tmp_DH); cleanup: TCN_FREE_CSTRING(cert); TCN_FREE_CSTRING(key); TCN_FREE_CSTRING(password); return rv; } TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCertificateRaw)(TCN_STDARGS, jlong ctx, jbyteArray javaCert, jbyteArray javaKey, jint idx) { #ifdef HAVE_ECC #ifndef SSL_CTX_set_ecdh_auto EC_KEY *eckey = NULL; #endif #endif jsize lengthOfCert; unsigned char* cert; X509 * certs; EVP_PKEY * evp; const unsigned char *tmp; BIO * bio; tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jboolean rv = JNI_TRUE; char err[256]; /* we get the key contents into a byte array */ jbyte* bufferPtr = (*e)->GetByteArrayElements(e, javaKey, NULL); jsize lengthOfKey = (*e)->GetArrayLength(e, javaKey); unsigned char* key = malloc(lengthOfKey); memcpy(key, bufferPtr, lengthOfKey); (*e)->ReleaseByteArrayElements(e, javaKey, bufferPtr, 0); bufferPtr = (*e)->GetByteArrayElements(e, javaCert, NULL); lengthOfCert = (*e)->GetArrayLength(e, javaCert); cert = malloc(lengthOfCert); memcpy(cert, bufferPtr, lengthOfCert); (*e)->ReleaseByteArrayElements(e, javaCert, bufferPtr, 0); UNREFERENCED(o); TCN_ASSERT(ctx != 0); if (idx < 0 || idx >= SSL_AIDX_MAX) { tcn_Throw(e, "Invalid key type"); rv = JNI_FALSE; goto cleanup; } tmp = (const unsigned char *)cert; certs = d2i_X509(NULL, &tmp, lengthOfCert); if (certs == NULL) { ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Error reading certificate (%s)", err); rv = JNI_FALSE; goto cleanup; } if(c->certs[idx] != NULL) { free(c->certs[idx]); } c->certs[idx] = certs; bio = BIO_new(BIO_s_mem()); BIO_write(bio, key, lengthOfKey); evp = PEM_read_bio_PrivateKey(bio, NULL, 0, NULL); if (evp == NULL) { BIO_free(bio); ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Error reading private key (%s)", err); rv = JNI_FALSE; goto cleanup; } BIO_free(bio); if(c->keys[idx] != NULL) { free(c->keys[idx]); } c->keys[idx] = evp; if (SSL_CTX_use_certificate(c->ctx, c->certs[idx]) <= 0) { ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Error setting certificate (%s)", err); rv = JNI_FALSE; goto cleanup; } if (SSL_CTX_use_PrivateKey(c->ctx, c->keys[idx]) <= 0) { ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Error setting private key (%s)", err); rv = JNI_FALSE; goto cleanup; } if (SSL_CTX_check_private_key(c->ctx) <= 0) { ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Private key does not match the certificate public key (%s)", err); rv = JNI_FALSE; goto cleanup; } /* * TODO Try to read DH parameters from somewhere... */ #ifdef HAVE_ECC /* * TODO try to read the ECDH curve name from somewhere... */ #if defined(SSL_CTX_set_ecdh_auto) SSL_CTX_set_ecdh_auto(c->ctx, 1); #else eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); SSL_CTX_set_tmp_ecdh(c->ctx, eckey); EC_KEY_free(eckey); #endif #endif SSL_CTX_set_tmp_dh_callback(c->ctx, SSL_callback_tmp_DH); cleanup: free(key); free(cert); return rv; } TCN_IMPLEMENT_CALL(jboolean, SSLContext, addChainCertificateRaw)(TCN_STDARGS, jlong ctx, jbyteArray javaCert) { jsize lengthOfCert; unsigned char* cert; X509 * certs; const unsigned char *tmp; tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jboolean rv = JNI_TRUE; char err[256]; /* we get the cert contents into a byte array */ jbyte* bufferPtr = (*e)->GetByteArrayElements(e, javaCert, NULL); lengthOfCert = (*e)->GetArrayLength(e, javaCert); cert = malloc(lengthOfCert); memcpy(cert, bufferPtr, lengthOfCert); (*e)->ReleaseByteArrayElements(e, javaCert, bufferPtr, 0); UNREFERENCED(o); TCN_ASSERT(ctx != 0); tmp = (const unsigned char *)cert; certs = d2i_X509(NULL, &tmp, lengthOfCert); if (certs == NULL) { ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Error reading certificate (%s)", err); rv = JNI_FALSE; } else if (SSL_CTX_add0_chain_cert(c->ctx, certs) <= 0) { ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Error adding certificate to chain (%s)", err); rv = JNI_FALSE; } free(cert); return rv; } static int ssl_array_index(apr_array_header_t *array, const char *s) { int i; for (i = 0; i < array->nelts; i++) { const char *p = APR_ARRAY_IDX(array, i, const char*); if (!strcmp(p, s)) { return i; } } return -1; } static int ssl_cmp_alpn_protos(apr_array_header_t *array, const char *proto1, const char *proto2) { int index1 = ssl_array_index(array, proto1); int index2 = ssl_array_index(array, proto2); if (index2 > index1) { return (index1 >= 0)? 1 : -1; } else if (index1 > index2) { return (index2 >= 0)? -1 : 1; } /* Both have the same index (-1 so neither listed by cient) compare * the names so that spdy3 gets precedence over spdy2. That makes * the outcome at least deterministic. */ return strcmp((const char *)proto1, (const char *)proto2); } /* * This callback function is executed when the TLS Application Layer * Protocol Negotiate Extension (ALPN, RFC 7301) is triggered by the client * hello, giving a list of desired protocol names (in descending preference) * to the server. * The callback has to select a protocol name or return an error if none of * the clients preferences is supported. * The selected protocol does not have to be on the client list, according * to RFC 7301, so no checks are performed. * The client protocol list is serialized as length byte followed by ascii * characters (not null-terminated), followed by the next protocol name. */ int cb_server_alpn(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) { tcn_ssl_ctxt_t *tcsslctx = (tcn_ssl_ctxt_t *)arg; tcn_ssl_conn_t *con = (tcn_ssl_conn_t *)SSL_get_app_data(ssl); apr_array_header_t *client_protos; apr_array_header_t *proposed_protos; int i; size_t len; if (inlen == 0) { // Client specified an empty protocol list. Nothing to negotiate. return SSL_TLSEXT_ERR_ALERT_FATAL; } client_protos = apr_array_make(con->pool , 0, sizeof(char *)); for (i = 0; i < inlen; /**/) { /* Grab length of next item from leading length byte */ unsigned int plen = in[i++]; if (plen + i > inlen) { // The protocol name extends beyond the declared length // of the protocol list. return SSL_TLSEXT_ERR_ALERT_FATAL; } APR_ARRAY_PUSH(client_protos, char*) = apr_pstrndup(con->pool, (const char *)in+i, plen); i += plen; } if (tcsslctx->alpn == NULL) { // Server supported protocol names not set. return SSL_TLSEXT_ERR_ALERT_FATAL; } if (tcsslctx->alpnlen == 0) { // Server supported protocols is an empty list return SSL_TLSEXT_ERR_ALERT_FATAL; } proposed_protos = apr_array_make(con->pool, 0, sizeof(char *)); for (i = 0; i < tcsslctx->alpnlen; /**/) { /* Grab length of next item from leading length byte */ unsigned int plen = tcsslctx->alpn[i++]; if (plen + i > tcsslctx->alpnlen) { // The protocol name extends beyond the declared length // of the protocol list. return SSL_TLSEXT_ERR_ALERT_FATAL; } APR_ARRAY_PUSH(proposed_protos, char*) = apr_pstrndup(con->pool, (const char *)tcsslctx->alpn+i, plen); i += plen; } if (proposed_protos->nelts <= 0) { // Should never happen. The server did not specify any protocols. return SSL_TLSEXT_ERR_ALERT_FATAL; } /* Now select the most preferred protocol from the proposals. */ *out = APR_ARRAY_IDX(proposed_protos, 0, const unsigned char *); for (i = 1; i < proposed_protos->nelts; ++i) { const char *proto = APR_ARRAY_IDX(proposed_protos, i, const char*); /* Do we prefer it over existing candidate? */ if (ssl_cmp_alpn_protos(client_protos, (const char *)*out, proto) < 0) { *out = (const unsigned char*)proto; } } len = strlen((const char*)*out); if (len > 255) { // Agreed protocol name too long return SSL_TLSEXT_ERR_ALERT_FATAL; } *outlen = (unsigned char)len; return SSL_TLSEXT_ERR_OK; } TCN_IMPLEMENT_CALL(jint, SSLContext, setALPN)(TCN_STDARGS, jlong ctx, jbyteArray buf, jint len) { tcn_ssl_ctxt_t *sslctx = J2P(ctx, tcn_ssl_ctxt_t *); sslctx->alpn = apr_pcalloc(sslctx->pool, len); (*e)->GetByteArrayRegion(e, buf, 0, len, (jbyte *)sslctx->alpn); sslctx->alpnlen = len; if (sslctx->mode == SSL_MODE_SERVER) { SSL_CTX_set_alpn_select_cb(sslctx->ctx, cb_server_alpn, sslctx); } else { /* * TODO: Implement client side call-back * SSL_CTX_set_next_proto_select_cb(sslctx->ctx, cb_request_alpn, sslctx); */ return APR_ENOTIMPL; } return 0; } /* Start of netty-tc-native add */ /* Convert protos to wire format */ static int initProtocols(JNIEnv *e, const tcn_ssl_ctxt_t *c, unsigned char **proto_data, unsigned int *proto_len, jobjectArray protos) { int i; unsigned char *p_data; /* * We start with allocate 128 bytes which should be good enough for most use-cases while still be pretty low. * We will call realloc to increate this if needed. */ size_t p_data_size = 128; size_t p_data_len = 0; jstring proto_string; const char *proto_chars; size_t proto_chars_len; int cnt; if (protos == NULL) { // Guard against NULL protos. return -1; } cnt = (*e)->GetArrayLength(e, protos); if (cnt == 0) { // if cnt is 0 we not need to continue and can just fail fast. return -1; } p_data = (unsigned char *) malloc(p_data_size); if (p_data == NULL) { // Not enough memory? return -1; } for (i = 0; i < cnt; ++i) { proto_string = (jstring) (*e)->GetObjectArrayElement(e, protos, i); proto_chars = (*e)->GetStringUTFChars(e, proto_string, 0); proto_chars_len = strlen(proto_chars); if (proto_chars_len > 0 && proto_chars_len <= MAX_ALPN_NPN_PROTO_SIZE) { // We need to add +1 as each protocol is prefixed by it's length (unsigned char). // For all except of the last one we already have the extra space as everything is // delimited by ','. p_data_len += 1 + proto_chars_len; if (p_data_len > p_data_size) { // double size p_data_size <<= 1; p_data = realloc(p_data, p_data_size); if (p_data == NULL) { // Not enough memory? (*e)->ReleaseStringUTFChars(e, proto_string, proto_chars); break; } } // Write the length of the protocol and then increment before memcpy the protocol itself. *p_data = proto_chars_len; ++p_data; memcpy(p_data, proto_chars, proto_chars_len); p_data += proto_chars_len; } // Release the string to prevent memory leaks (*e)->ReleaseStringUTFChars(e, proto_string, proto_chars); } if (p_data == NULL) { // Something went wrong so update the proto_len and return -1 *proto_len = 0; return -1; } else { if (*proto_data != NULL) { // Free old data free(*proto_data); } // Decrement pointer again as we incremented it while creating the protocols in wire format. p_data -= p_data_len; *proto_data = p_data; *proto_len = p_data_len; return 0; } } TCN_IMPLEMENT_CALL(void, SSLContext, setNpnProtos)(TCN_STDARGS, jlong ctx, jobjectArray next_protos, jint selectorFailureBehavior) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); TCN_ASSERT(ctx != 0); UNREFERENCED(o); if (initProtocols(e, c, &c->next_proto_data, &c->next_proto_len, next_protos) == 0) { c->next_selector_failure_behavior = selectorFailureBehavior; // depending on if it's client mode or not we need to call different functions. if (c->mode == SSL_MODE_CLIENT) { SSL_CTX_set_next_proto_select_cb(c->ctx, SSL_callback_select_next_proto, (void *)c); } else { SSL_CTX_set_next_protos_advertised_cb(c->ctx, SSL_callback_next_protos, (void *)c); } } } TCN_IMPLEMENT_CALL(void, SSLContext, setAlpnProtos)(TCN_STDARGS, jlong ctx, jobjectArray alpn_protos, jint selectorFailureBehavior) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); TCN_ASSERT(ctx != 0); UNREFERENCED(o); if (initProtocols(e, c, &c->alpn_proto_data, &c->alpn_proto_len, alpn_protos) == 0) { c->alpn_selector_failure_behavior = selectorFailureBehavior; // depending on if it's client mode or not we need to call different functions. if (c->mode == SSL_MODE_CLIENT) { SSL_CTX_set_alpn_protos(c->ctx, c->alpn_proto_data, c->alpn_proto_len); } else { SSL_CTX_set_alpn_select_cb(c->ctx, SSL_callback_alpn_select_proto, (void *) c); } } } TCN_IMPLEMENT_CALL(jlong, SSLContext, setSessionCacheMode)(TCN_STDARGS, jlong ctx, jlong mode) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); return SSL_CTX_set_session_cache_mode(c->ctx, mode); } TCN_IMPLEMENT_CALL(jlong, SSLContext, getSessionCacheMode)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); return SSL_CTX_get_session_cache_mode(c->ctx); } TCN_IMPLEMENT_CALL(jlong, SSLContext, setSessionCacheTimeout)(TCN_STDARGS, jlong ctx, jlong timeout) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = SSL_CTX_set_timeout(c->ctx, timeout); return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, getSessionCacheTimeout)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); return SSL_CTX_get_timeout(c->ctx); } TCN_IMPLEMENT_CALL(jlong, SSLContext, setSessionCacheSize)(TCN_STDARGS, jlong ctx, jlong size) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = 0; // Also allow size of 0 which is unlimited if (size >= 0) { SSL_CTX_set_session_cache_mode(c->ctx, SSL_SESS_CACHE_SERVER); rv = SSL_CTX_sess_set_cache_size(c->ctx, size); } return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, getSessionCacheSize)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); return SSL_CTX_sess_get_cache_size(c->ctx); } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionNumber)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = SSL_CTX_sess_number(c->ctx); return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionConnect)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = SSL_CTX_sess_connect(c->ctx); return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionConnectGood)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = SSL_CTX_sess_connect_good(c->ctx); return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionConnectRenegotiate)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = SSL_CTX_sess_connect_renegotiate(c->ctx); return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionAccept)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = SSL_CTX_sess_accept(c->ctx); return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionAcceptGood)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = SSL_CTX_sess_accept_good(c->ctx); return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionAcceptRenegotiate)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = SSL_CTX_sess_accept_renegotiate(c->ctx); return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionHits)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = SSL_CTX_sess_hits(c->ctx); return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionCbHits)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = SSL_CTX_sess_cb_hits(c->ctx); return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionMisses)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = SSL_CTX_sess_misses(c->ctx); return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionTimeouts)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = SSL_CTX_sess_timeouts(c->ctx); return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionCacheFull)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = SSL_CTX_sess_cache_full(c->ctx); return rv; } #define TICKET_KEYS_SIZE 48 TCN_IMPLEMENT_CALL(void, SSLContext, setSessionTicketKeys)(TCN_STDARGS, jlong ctx, jbyteArray keys) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jbyte* b; if ((*e)->GetArrayLength(e, keys) != TICKET_KEYS_SIZE) { if (c->bio_os) { BIO_printf(c->bio_os, "[ERROR] Session ticket keys provided were wrong size."); } else { fprintf(stderr, "[ERROR] Session ticket keys provided were wrong size."); } exit(1); } b = (*e)->GetByteArrayElements(e, keys, NULL); SSL_CTX_set_tlsext_ticket_keys(c->ctx, b, TICKET_KEYS_SIZE); (*e)->ReleaseByteArrayElements(e, keys, b, 0); } #if OPENSSL_VERSION_NUMBER < 0x10100000L /* * Adapted from OpenSSL: * https://github.com/openssl/openssl/blob/OpenSSL_1_0_2-stable/ssl/ssl_locl.h#L318 */ /* Bits for algorithm_mkey (key exchange algorithm) */ /* RSA key exchange */ # define SSL_kRSA 0x00000001L /* DH cert, RSA CA cert */ # define SSL_kDHr 0x00000002L /* DH cert, DSA CA cert */ # define SSL_kDHd 0x00000004L /* tmp DH key no DH cert */ # define SSL_kEDH 0x00000008L /* forward-compatible synonym */ # define SSL_kDHE SSL_kEDH /* Kerberos5 key exchange */ # define SSL_kKRB5 0x00000010L /* ECDH cert, RSA CA cert */ # define SSL_kECDHr 0x00000020L /* ECDH cert, ECDSA CA cert */ # define SSL_kECDHe 0x00000040L /* ephemeral ECDH */ # define SSL_kEECDH 0x00000080L /* forward-compatible synonym */ # define SSL_kECDHE SSL_kEECDH /* PSK */ # define SSL_kPSK 0x00000100L /* GOST key exchange */ # define SSL_kGOST 0x00000200L /* SRP */ # define SSL_kSRP 0x00000400L /* Bits for algorithm_auth (server authentication) */ /* RSA auth */ # define SSL_aRSA 0x00000001L /* DSS auth */ # define SSL_aDSS 0x00000002L /* no auth (i.e. use ADH or AECDH) */ # define SSL_aNULL 0x00000004L /* Fixed DH auth (kDHd or kDHr) */ # define SSL_aDH 0x00000008L /* Fixed ECDH auth (kECDHe or kECDHr) */ # define SSL_aECDH 0x00000010L /* KRB5 auth */ # define SSL_aKRB5 0x00000020L /* ECDSA auth*/ # define SSL_aECDSA 0x00000040L /* PSK auth */ # define SSL_aPSK 0x00000080L /* GOST R 34.10-94 signature auth */ # define SSL_aGOST94 0x00000100L /* GOST R 34.10-2001 signature auth */ # define SSL_aGOST01 0x00000200L /* SRP auth */ # define SSL_aSRP 0x00000400L /* OpenSSL end */ #define TCN_SSL_kRSA SSL_kRSA #define TCN_SSL_kDHr SSL_kDHr #define TCN_SSL_kDHd SSL_kDHd #define TCN_SSL_kDHE SSL_kDHE #define TCN_SSL_kKRB5 SSL_kKRB5 #define TCN_SSL_kECDHr SSL_kECDHr #define TCN_SSL_kECDHe SSL_kECDHe #define TCN_SSL_kECDHE SSL_kECDHE #define TCN_SSL_aRSA SSL_aRSA #define TCN_SSL_aDSS SSL_aDSS #define TCN_SSL_aNULL SSL_aNULL #define TCN_SSL_aDH SSL_aDH #define TCN_SSL_aECDH SSL_aECDH #define TCN_SSL_aKRB5 SSL_aKRB5 #define TCN_SSL_aECDSA SSL_aECDSA #else #define TCN_SSL_kRSA NID_kx_rsa #define TCN_SSL_kDHE NID_kx_dhe #define TCN_SSL_kECDHE NID_kx_ecdhe #define TCN_SSL_aRSA NID_auth_rsa #define TCN_SSL_aDSS NID_auth_dss #define TCN_SSL_aNULL NID_auth_null #define TCN_SSL_aECDSA NID_auth_ecdsa #endif /* * Adapted from Android: * https://android.googlesource.com/platform/external/openssl/+/master/patches/0003-jsse.patch */ static const char* SSL_CIPHER_authentication_method(const SSL_CIPHER* cipher){ int auth; int kx; if (cipher == NULL) { return "UNKNOWN"; } #if OPENSSL_VERSION_NUMBER < 0x10100000L kx = cipher->algorithm_mkey; auth = cipher->algorithm_auth; #else kx = SSL_CIPHER_get_kx_nid(cipher); auth = SSL_CIPHER_get_auth_nid(cipher); #endif switch (kx) { case TCN_SSL_kRSA: return SSL_TXT_RSA; #if OPENSSL_VERSION_NUMBER < 0x10100000L case TCN_SSL_kDHr: return SSL_TXT_DH "_" SSL_TXT_RSA; case TCN_SSL_kDHd: return SSL_TXT_DH "_" SSL_TXT_DSS; #endif case TCN_SSL_kDHE: switch (auth) { case TCN_SSL_aDSS: return "DHE_" SSL_TXT_DSS; case TCN_SSL_aRSA: return "DHE_" SSL_TXT_RSA; case TCN_SSL_aNULL: return SSL_TXT_DH "_anon"; default: return "UNKNOWN"; } #if OPENSSL_VERSION_NUMBER < 0x10100000L case TCN_SSL_kKRB5: return SSL_TXT_KRB5; case TCN_SSL_kECDHr: return SSL_TXT_ECDH "_" SSL_TXT_RSA; case TCN_SSL_kECDHe: return SSL_TXT_ECDH "_" SSL_TXT_ECDSA; #endif case TCN_SSL_kECDHE: switch (auth) { case TCN_SSL_aECDSA: return "ECDHE_" SSL_TXT_ECDSA; case TCN_SSL_aRSA: return "ECDHE_" SSL_TXT_RSA; case TCN_SSL_aNULL: return SSL_TXT_ECDH "_anon"; default: return "UNKNOWN"; } default: return "UNKNOWN"; } } static const char* SSL_authentication_method(const SSL* ssl) { #if OPENSSL_VERSION_NUMBER < 0x10100000L return SSL_CIPHER_authentication_method(ssl->s3->tmp.new_cipher); #else /* XXX ssl->s3->tmp.new_cipher is no longer available in OpenSSL 1.1.0 */ /* https://github.com/netty/netty-tcnative/blob/1.1.33/openssl-dynamic/src/main/c/sslcontext.c * contains a different method, but I think this is not correct. * Instead of choosing the cipher used for the current handshake it simply * uses the first cipher available during the handshake. */ /* Not sure whether SSL_get_current_cipher(ssl) returns something useful * at the point in time we call it. */ return SSL_CIPHER_authentication_method(SSL_get_current_cipher(ssl)); #endif } /* Android end */ static int SSL_cert_verify(X509_STORE_CTX *ctx, void *arg) { /* Get Apache context back through OpenSSL context */ SSL *ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); tcn_ssl_ctxt_t *c = SSL_get_app_data2(ssl); // Get a stack of all certs in the chain STACK_OF(X509) *sk = X509_STORE_CTX_get0_untrusted(ctx); int len = sk_X509_num(sk); unsigned i; X509 *cert; int length; unsigned char *buf; JNIEnv *e; jbyteArray array; jbyteArray bArray; const char *authMethod; jstring authMethodString; jboolean result; int r; tcn_get_java_env(&e); // Create the byte[][] array that holds all the certs array = (*e)->NewObjectArray(e, len, byteArrayClass, NULL); for(i = 0; i < len; i++) { cert = (X509*) sk_X509_value(sk, i); buf = NULL; length = i2d_X509(cert, &buf); if (length < 0) { // In case of error just return an empty byte[][] array = (*e)->NewObjectArray(e, 0, byteArrayClass, NULL); // We need to delete the local references so we not leak memory as this method is called via callback. OPENSSL_free(buf); break; } bArray = (*e)->NewByteArray(e, length); (*e)->SetByteArrayRegion(e, bArray, 0, length, (jbyte*) buf); (*e)->SetObjectArrayElement(e, array, i, bArray); // Delete the local reference as we not know how long the chain is and local references are otherwise // only freed once jni method returns. (*e)->DeleteLocalRef(e, bArray); OPENSSL_free(buf); } authMethod = SSL_authentication_method(ssl); authMethodString = (*e)->NewStringUTF(e, authMethod); result = (*e)->CallBooleanMethod(e, c->verifier, c->verifier_method, P2J(ssl), array, authMethodString); r = result == JNI_TRUE ? 1 : 0; // We need to delete the local references so we not leak memory as this method is called via callback. (*e)->DeleteLocalRef(e, authMethodString); (*e)->DeleteLocalRef(e, array); return r; } TCN_IMPLEMENT_CALL(void, SSLContext, setCertVerifyCallback)(TCN_STDARGS, jlong ctx, jobject verifier) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); UNREFERENCED(o); TCN_ASSERT(ctx != 0); if (verifier == NULL) { SSL_CTX_set_cert_verify_callback(c->ctx, NULL, NULL); } else { jclass verifier_class = (*e)->GetObjectClass(e, verifier); jmethodID method = (*e)->GetMethodID(e, verifier_class, "verify", "(J[[BLjava/lang/String;)Z"); if (method == NULL) { return; } // Delete the reference to the previous specified verifier if needed. if (c->verifier != NULL) { (*e)->DeleteLocalRef(e, c->verifier); } c->verifier = (*e)->NewGlobalRef(e, verifier); c->verifier_method = method; SSL_CTX_set_cert_verify_callback(c->ctx, SSL_cert_verify, NULL); } } TCN_IMPLEMENT_CALL(jboolean, SSLContext, setSessionIdContext)(TCN_STDARGS, jlong ctx, jbyteArray sidCtx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); int len = (*e)->GetArrayLength(e, sidCtx); unsigned char *buf; int res; UNREFERENCED(o); TCN_ASSERT(ctx != 0); buf = malloc(len); (*e)->GetByteArrayRegion(e, sidCtx, 0, len, (jbyte*) buf); res = SSL_CTX_set_session_id_context(c->ctx, buf, len); free(buf); if (res == 1) { return JNI_TRUE; } return JNI_FALSE; } /* End of netty-tc-native add */ #else /* OpenSSL is not supported. * Create empty stubs. */ TCN_IMPLEMENT_CALL(jlong, SSLContext, make)(TCN_STDARGS, jlong pool, jint protocol, jint mode) { UNREFERENCED_STDARGS; UNREFERENCED(pool); UNREFERENCED(protocol); UNREFERENCED(mode); return 0; } TCN_IMPLEMENT_CALL(jint, SSLContext, free)(TCN_STDARGS, jlong ctx) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); return APR_ENOTIMPL; } TCN_IMPLEMENT_CALL(void, SSLContext, setContextId)(TCN_STDARGS, jlong ctx, jstring id) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); UNREFERENCED(id); } TCN_IMPLEMENT_CALL(void, SSLContext, setBIO)(TCN_STDARGS, jlong ctx, jlong bio, jint dir) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); UNREFERENCED(bio); UNREFERENCED(dir); } TCN_IMPLEMENT_CALL(void, SSLContext, setOptions)(TCN_STDARGS, jlong ctx, jint opt) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); UNREFERENCED(opt); } TCN_IMPLEMENT_CALL(jint, SSLContext, getOptions)(TCN_STDARGS, jlong ctx) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); } TCN_IMPLEMENT_CALL(void, SSLContext, clearOptions)(TCN_STDARGS, jlong ctx, jint opt) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); UNREFERENCED(opt); } TCN_IMPLEMENT_CALL(void, SSLContext, setQuietShutdown)(TCN_STDARGS, jlong ctx, jboolean mode) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); UNREFERENCED(mode); } TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCipherSuite)(TCN_STDARGS, jlong ctx, jstring ciphers) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); UNREFERENCED(ciphers); return JNI_FALSE; } TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCARevocation)(TCN_STDARGS, jlong ctx, jstring file, jstring path) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); UNREFERENCED(file); UNREFERENCED(path); return JNI_FALSE; } TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCertificateChainFile)(TCN_STDARGS, jlong ctx, jstring file, jboolean skipfirst) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); UNREFERENCED(file); UNREFERENCED(skipfirst); return JNI_FALSE; } TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCACertificate)(TCN_STDARGS, jlong ctx, jstring file, jstring path) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); UNREFERENCED(file); UNREFERENCED(path); return JNI_FALSE; } TCN_IMPLEMENT_CALL(void, SSLContext, setShutdownType)(TCN_STDARGS, jlong ctx, jint type) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); UNREFERENCED(type); } TCN_IMPLEMENT_CALL(void, SSLContext, setVerify)(TCN_STDARGS, jlong ctx, jint level, jint depth) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); UNREFERENCED(level); UNREFERENCED(depth); } TCN_IMPLEMENT_CALL(void, SSLContext, setRandom)(TCN_STDARGS, jlong ctx, jstring file) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); UNREFERENCED(file); } TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCertificate)(TCN_STDARGS, jlong ctx, jstring cert, jstring key, jstring password, jint idx) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); UNREFERENCED(cert); UNREFERENCED(key); UNREFERENCED(password); UNREFERENCED(idx); return JNI_FALSE; } TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCertificateRaw)(TCN_STDARGS, jlong ctx, jbyteArray javaCert, jbyteArray javaKey, jint idx) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); UNREFERENCED(javaCert); UNREFERENCED(javaKey); UNREFERENCED(idx); return JNI_FALSE; } TCN_IMPLEMENT_CALL(jboolean, SSLContext, addChainCertificateRaw)(TCN_STDARGS, jlong ctx, jbyteArray javaCert) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); UNREFERENCED(javaCert); return JNI_FALSE; } TCN_IMPLEMENT_CALL(jint, SSLContext, setALPN)(TCN_STDARGS, jlong ctx, jbyteArray buf, jint len) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); UNREFERENCED(buf); UNREFERENCED(len); return APR_ENOTIMPL; } /* Start of netty-tc-native add */ TCN_IMPLEMENT_CALL(void, SSLContext, setNpnProtos)(TCN_STDARGS, jlong ctx, jobjectArray next_protos, jint selectorFailureBehavior) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); UNREFERENCED(next_protos); } TCN_IMPLEMENT_CALL(void, SSLContext, setAlpnProtos)(TCN_STDARGS, jlong ctx, jobjectArray alpn_protos, jint selectorFailureBehavior) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); UNREFERENCED(alpn_protos); } TCN_IMPLEMENT_CALL(jlong, SSLContext, setSessionCacheMode)(TCN_STDARGS, jlong ctx, jlong mode) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); UNREFERENCED(mode); return -1; } TCN_IMPLEMENT_CALL(jlong, SSLContext, getSessionCacheMode)(TCN_STDARGS, jlong ctx) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); return -1; } TCN_IMPLEMENT_CALL(jlong, SSLContext, setSessionCacheTimeout)(TCN_STDARGS, jlong ctx, jlong timeout) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); UNREFERENCED(timeout); return -1; } TCN_IMPLEMENT_CALL(jlong, SSLContext, getSessionCacheTimeout)(TCN_STDARGS, jlong ctx) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); return -1; } TCN_IMPLEMENT_CALL(jlong, SSLContext, setSessionCacheSize)(TCN_STDARGS, jlong ctx, jlong size) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); UNREFERENCED(size); return -1; } TCN_IMPLEMENT_CALL(jlong, SSLContext, getSessionCacheSize)(TCN_STDARGS, jlong ctx) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); return -1; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionNumber)(TCN_STDARGS, jlong ctx) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); return 0; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionConnect)(TCN_STDARGS, jlong ctx) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); return 0; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionConnectGood)(TCN_STDARGS, jlong ctx) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); return 0; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionConnectRenegotiate)(TCN_STDARGS, jlong ctx) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); return 0; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionAccept)(TCN_STDARGS, jlong ctx) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); return 0; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionAcceptGood)(TCN_STDARGS, jlong ctx) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); return 0; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionAcceptRenegotiate)(TCN_STDARGS, jlong ctx) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); return 0; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionHits)(TCN_STDARGS, jlong ctx) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); return 0; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionCbHits)(TCN_STDARGS, jlong ctx) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); return 0; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionTimeouts)(TCN_STDARGS, jlong ctx) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); return 0; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionCacheFull)(TCN_STDARGS, jlong ctx) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); return 0; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionMisses)(TCN_STDARGS, jlong ctx) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); return 0; } TCN_IMPLEMENT_CALL(void, SSLContext, setSessionTicketKeys)(TCN_STDARGS, jlong ctx, jbyteArray keys) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); UNREFERENCED(keys); } TCN_IMPLEMENT_CALL(void, SSLContext, setCertVerifyCallback)(TCN_STDARGS, jlong ctx, jobject verifier) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); UNREFERENCED(verifier); } TCN_IMPLEMENT_CALL(jboolean, SSLContext, setSessionIdContext)(TCN_STDARGS, jlong ctx, jbyteArray sidCtx) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); UNREFERENCED(sidCtx); return JNI_FALSE; } /* End of netty-tc-native add */ #endif