| Index: c/sslcontext.c
|
| diff --git a/c/sslcontext.c b/c/sslcontext.c
|
| index 2f42eb459d070704a55f34c231b582f2807cde91..25bfb6e97977100f5c99f5772d27a675a42f9b3d 100644
|
| --- a/c/sslcontext.c
|
| +++ b/c/sslcontext.c
|
| @@ -1,3 +1,18 @@
|
| +/*
|
| + * Copyright 2016 The Netty Project
|
| + *
|
| + * The Netty Project 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.
|
| + */
|
| /* 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.
|
| @@ -14,23 +29,15 @@
|
| * limitations under the License.
|
| */
|
|
|
| -/** SSL Context wrapper
|
| - *
|
| - * @author Mladen Turk
|
| - * @version $Id: sslcontext.c 1649733 2015-01-06 04:42:24Z billbarker $
|
| - */
|
| -
|
| #include "tcn.h"
|
|
|
| -#include "apr_file_io.h"
|
| -#include "apr_thread_mutex.h"
|
| #include "apr_thread_rwlock.h"
|
| -#include "apr_poll.h"
|
| +#include "apr_atomic.h"
|
|
|
| -#ifdef HAVE_OPENSSL
|
| #include "ssl_private.h"
|
| +#include <stdint.h>
|
|
|
| -static jclass byteArrayClass;
|
| +extern apr_pool_t *tcn_global_pool;
|
|
|
| static apr_status_t ssl_context_cleanup(void *data)
|
| {
|
| @@ -38,46 +45,30 @@ static apr_status_t ssl_context_cleanup(void *data)
|
| JNIEnv *e;
|
|
|
| if (c) {
|
| - int i;
|
| - if (c->crl)
|
| - X509_STORE_free(c->crl);
|
| - c->crl = NULL;
|
| - if (c->ctx)
|
| - SSL_CTX_free(c->ctx);
|
| + SSL_CTX_free(c->ctx); // this function is safe to call with NULL
|
| 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) {
|
| + if (c->verifier != NULL) {
|
| tcn_get_java_env(&e);
|
| (*e)->DeleteGlobalRef(e, c->verifier);
|
| c->verifier = NULL;
|
| }
|
| c->verifier_method = NULL;
|
|
|
| - if (c->next_proto_data) {
|
| + if (c->cert_requested_callback != NULL) {
|
| + tcn_get_java_env(&e);
|
| + (*e)->DeleteGlobalRef(e, c->cert_requested_callback);
|
| + c->cert_requested_callback = NULL;
|
| + }
|
| + c->cert_requested_callback_method = NULL;
|
| +
|
| + if (c->next_proto_data != NULL) {
|
| free(c->next_proto_data);
|
| c->next_proto_data = NULL;
|
| }
|
| c->next_proto_len = 0;
|
|
|
| - if (c->alpn_proto_data) {
|
| + if (c->alpn_proto_data != NULL) {
|
| free(c->alpn_proto_data);
|
| c->alpn_proto_data = NULL;
|
| }
|
| @@ -85,28 +76,41 @@ static apr_status_t ssl_context_cleanup(void *data)
|
|
|
| apr_thread_rwlock_destroy(c->mutex);
|
|
|
| - if (c->ticket_keys) {
|
| + if (c->ticket_keys != NULL) {
|
| free(c->ticket_keys);
|
| c->ticket_keys = NULL;
|
| }
|
| c->ticket_keys_len = 0;
|
| +
|
| + if (c->password != NULL) {
|
| + free(c->password);
|
| + c->password = NULL;
|
| + }
|
| }
|
| return APR_SUCCESS;
|
| }
|
|
|
| /* Initialize server context */
|
| -TCN_IMPLEMENT_CALL(jlong, SSLContext, make)(TCN_STDARGS, jlong pool,
|
| - jint protocol, jint mode)
|
| +TCN_IMPLEMENT_CALL(jlong, SSLContext, make)(TCN_STDARGS, jint protocol, jint mode)
|
| {
|
| - apr_pool_t *p = J2P(pool, apr_pool_t *);
|
| + apr_pool_t *p = NULL;
|
| tcn_ssl_ctxt_t *c = NULL;
|
| SSL_CTX *ctx = NULL;
|
| - jclass clazz;
|
|
|
| UNREFERENCED(o);
|
|
|
| - if (protocol == SSL_PROTOCOL_TLSV1_2) {
|
| -#ifdef SSL_OP_NO_TLSv1_2
|
| + switch (protocol) {
|
| + case SSL_PROTOCOL_TLS:
|
| + case SSL_PROTOCOL_ALL:
|
| + if (mode == SSL_MODE_CLIENT)
|
| + ctx = SSL_CTX_new(SSLv23_client_method());
|
| + else if (mode == SSL_MODE_SERVER)
|
| + ctx = SSL_CTX_new(SSLv23_server_method());
|
| + else
|
| + ctx = SSL_CTX_new(SSLv23_method());
|
| + break;
|
| + case SSL_PROTOCOL_TLSV1_2:
|
| +#ifndef OPENSSL_NO_TLS1
|
| if (mode == SSL_MODE_CLIENT)
|
| ctx = SSL_CTX_new(TLSv1_2_client_method());
|
| else if (mode == SSL_MODE_SERVER)
|
| @@ -114,8 +118,9 @@ TCN_IMPLEMENT_CALL(jlong, SSLContext, make)(TCN_STDARGS, jlong pool,
|
| else
|
| ctx = SSL_CTX_new(TLSv1_2_method());
|
| #endif
|
| - } else if (protocol == SSL_PROTOCOL_TLSV1_1) {
|
| -#ifdef SSL_OP_NO_TLSv1_1
|
| + break;
|
| + case SSL_PROTOCOL_TLSV1_1:
|
| +#ifndef OPENSSL_NO_TLS1
|
| if (mode == SSL_MODE_CLIENT)
|
| ctx = SSL_CTX_new(TLSv1_1_client_method());
|
| else if (mode == SSL_MODE_SERVER)
|
| @@ -123,15 +128,19 @@ TCN_IMPLEMENT_CALL(jlong, SSLContext, make)(TCN_STDARGS, jlong pool,
|
| else
|
| ctx = SSL_CTX_new(TLSv1_1_method());
|
| #endif
|
| - } else if (protocol == SSL_PROTOCOL_TLSV1) {
|
| + break;
|
| + case SSL_PROTOCOL_TLSV1:
|
| +#ifndef OPENSSL_NO_TLS1
|
| 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());
|
| +#endif
|
| + break;
|
| + case SSL_PROTOCOL_SSLV3:
|
| #ifndef OPENSSL_NO_SSL3
|
| - } else if (protocol == SSL_PROTOCOL_SSLV3) {
|
| if (mode == SSL_MODE_CLIENT)
|
| ctx = SSL_CTX_new(SSLv3_client_method());
|
| else if (mode == SSL_MODE_SERVER)
|
| @@ -139,8 +148,9 @@ TCN_IMPLEMENT_CALL(jlong, SSLContext, make)(TCN_STDARGS, jlong pool,
|
| else
|
| ctx = SSL_CTX_new(SSLv3_method());
|
| #endif
|
| + break;
|
| + case SSL_PROTOCOL_SSLV2:
|
| #if (OPENSSL_VERSION_NUMBER < 0x10100000L) && !defined(OPENSSL_NO_SSL2)
|
| - } else if (protocol == SSL_PROTOCOL_SSLV2) {
|
| if (mode == SSL_MODE_CLIENT)
|
| ctx = SSL_CTX_new(SSLv2_client_method());
|
| else if (mode == SSL_MODE_SERVER)
|
| @@ -148,40 +158,80 @@ TCN_IMPLEMENT_CALL(jlong, SSLContext, make)(TCN_STDARGS, jlong pool,
|
| else
|
| ctx = SSL_CTX_new(SSLv2_method());
|
| #endif
|
| -#ifndef SSL_OP_NO_TLSv1_2
|
| - } else if (protocol & SSL_PROTOCOL_TLSV1_2) {
|
| - /* requested but not supported */
|
| + break;
|
| + default:
|
| + // Try to give the user the highest supported protocol.
|
| +#ifndef OPENSSL_NO_TLS1
|
| + if (protocol & SSL_PROTOCOL_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());
|
| + break;
|
| + } else if (protocol & SSL_PROTOCOL_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());
|
| + break;
|
| + } 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());
|
| + break;
|
| + }
|
| +#endif
|
| +#ifndef OPENSSL_NO_SSL3
|
| + 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());
|
| + break;
|
| + }
|
| #endif
|
| -#ifndef SSL_OP_NO_TLSv1_1
|
| - } else if (protocol & SSL_PROTOCOL_TLSV1_1) {
|
| - /* requested but not supported */
|
| +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) && !defined(OPENSSL_NO_SSL2)
|
| + if (protocol & SSL_PROTOCOL_SSLV2) {
|
| + if (mode == SSL_MODE_CLIENT)
|
| + ctx = SSL_CTX_new(SSLv2_client_method());
|
| + else if (mode == SSL_MODE_SERVER)
|
| + ctx = SSL_CTX_new(SSLv2_server_method());
|
| + else
|
| + ctx = SSL_CTX_new(SSLv2_method());
|
| + break;
|
| + }
|
| #endif
|
| - } else {
|
| - if (mode == SSL_MODE_CLIENT)
|
| - ctx = SSL_CTX_new(SSLv23_client_method());
|
| - else if (mode == SSL_MODE_SERVER)
|
| - ctx = SSL_CTX_new(SSLv23_server_method());
|
| - else
|
| - ctx = SSL_CTX_new(SSLv23_method());
|
| + tcn_Throw(e, "Unsupported SSL protocol (%d)", protocol);
|
| + goto cleanup;
|
| }
|
|
|
| - if (!ctx) {
|
| + if (ctx == NULL) {
|
| char err[256];
|
| ERR_error_string(ERR_get_error(), err);
|
| - tcn_Throw(e, "Invalid Server SSL Protocol (%s)", err);
|
| - goto init_failed;
|
| + tcn_Throw(e, "Failed to initialize SSL_CTX (%s)", err);
|
| + goto cleanup;
|
| }
|
| +
|
| + TCN_THROW_IF_ERR(apr_pool_create(&p, tcn_global_pool), p);
|
| +
|
| if ((c = apr_pcalloc(p, sizeof(tcn_ssl_ctxt_t))) == NULL) {
|
| tcn_ThrowAPRException(e, apr_get_os_error());
|
| - goto init_failed;
|
| + goto cleanup;
|
| }
|
|
|
| c->protocol = protocol;
|
| c->mode = mode;
|
| c->ctx = ctx;
|
| c->pool = p;
|
| - c->bio_os = NULL;
|
| - SSL_CTX_set_options(c->ctx, SSL_OP_ALL);
|
| if (!(protocol & SSL_PROTOCOL_SSLV2))
|
| SSL_CTX_set_options(c->ctx, SSL_OP_NO_SSLv2);
|
| if (!(protocol & SSL_PROTOCOL_SSLV3))
|
| @@ -204,28 +254,22 @@ TCN_IMPLEMENT_CALL(jlong, SSLContext, make)(TCN_STDARGS, jlong pool,
|
| SSL_CTX_set_options(c->ctx, SSL_OP_SINGLE_ECDH_USE);
|
| #endif
|
|
|
| -#ifdef SSL_OP_NO_COMPRESSION
|
| SSL_CTX_set_options(c->ctx, SSL_OP_NO_COMPRESSION);
|
| -#else
|
| -#error "SSL_OP_NO_COMPRESSION not supported in your version of OpenSSL"
|
| -#endif
|
|
|
| -#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);
|
| -#else
|
| -#error "SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION not supported in your version of OpenSSL"
|
| -#endif
|
| + /**
|
| + * These options may be set by default but can be dangerous in practice [1].
|
| + * [1] https://www.openssl.org/docs/man1.0.1/ssl/SSL_CTX_set_options.html
|
| + */
|
| + SSL_CTX_clear_options(c->ctx, SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION | SSL_OP_LEGACY_SERVER_CONNECT);
|
|
|
| -#ifdef SSL_MODE_RELEASE_BUFFERS
|
| /* Release idle buffers to the SSL_CTX free list */
|
| SSL_CTX_set_mode(c->ctx, SSL_MODE_RELEASE_BUFFERS);
|
| -#else
|
| -#error "SSL_MODE_RELEASE_BUFFERS not supported in your version of OpenSSL"
|
| -#endif
|
| +
|
| /* Default session context id and cache size */
|
| SSL_CTX_sess_set_cache_size(c->ctx, SSL_DEFAULT_CACHE_SIZE);
|
|
|
| @@ -244,26 +288,16 @@ TCN_IMPLEMENT_CALL(jlong, SSLContext, make)(TCN_STDARGS, jlong pool,
|
| EC_KEY_free(ecdh);
|
| #endif
|
|
|
| -// This method should not be used when using libressl as it will result in
|
| -// error:14085042:SSL routines:SSL3_CTX_CTRL:called a function you should not call
|
| -//
|
| -// See also http://forum.nginx.org/read.php?2,256381,257336#msg-257336
|
| -#ifndef LIBRESSL_VERSION_NUMBER
|
| - SSL_CTX_set_tmp_rsa_callback(c->ctx, SSL_callback_tmp_RSA);
|
| -#endif
|
| SSL_CTX_set_tmp_dh_callback(c->ctx, SSL_callback_tmp_DH);
|
| }
|
| - /* 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;
|
| +
|
| + // Default depth is 100 and disabled according to https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_verify.html.
|
| + c->verify_config.verify_depth = 100;
|
| + c->verify_config.verify_mode = SSL_CVERIFY_NONE;
|
|
|
| /* 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);
|
| + SSL_CTX_set_default_passwd_cb_userdata(c->ctx, (void *) c->password);
|
|
|
| apr_thread_rwlock_create(&c->mutex, p);
|
| /*
|
| @@ -273,13 +307,12 @@ TCN_IMPLEMENT_CALL(jlong, SSLContext, make)(TCN_STDARGS, jlong pool,
|
| 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:
|
| +cleanup:
|
| + if (p != NULL) {
|
| + apr_pool_destroy(p);
|
| + }
|
| + SSL_CTX_free(ctx); // this function is safe to call with NULL.
|
| return 0;
|
| }
|
|
|
| @@ -289,7 +322,9 @@ TCN_IMPLEMENT_CALL(jint, SSLContext, free)(TCN_STDARGS, jlong ctx)
|
| UNREFERENCED_STDARGS;
|
| TCN_ASSERT(ctx != 0);
|
| /* Run and destroy the cleanup callback */
|
| - return apr_pool_cleanup_run(c->pool, c, ssl_context_cleanup);
|
| + int result = apr_pool_cleanup_run(c->pool, c, ssl_context_cleanup);
|
| + apr_pool_destroy(c->pool);
|
| + return result;
|
| }
|
|
|
| TCN_IMPLEMENT_CALL(void, SSLContext, setContextId)(TCN_STDARGS, jlong ctx,
|
| @@ -308,29 +343,6 @@ TCN_IMPLEMENT_CALL(void, SSLContext, setContextId)(TCN_STDARGS, jlong ctx,
|
| 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)
|
| {
|
| @@ -338,11 +350,7 @@ TCN_IMPLEMENT_CALL(void, SSLContext, setOptions)(TCN_STDARGS, jlong ctx,
|
|
|
| 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);
|
| }
|
|
|
| @@ -366,16 +374,6 @@ TCN_IMPLEMENT_CALL(void, SSLContext, clearOptions)(TCN_STDARGS, jlong ctx,
|
| 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)
|
| {
|
| @@ -398,55 +396,6 @@ TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCipherSuite)(TCN_STDARGS, jlong ctx,
|
| 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 ((c->crl = X509_STORE_new()) == NULL)
|
| - goto cleanup;
|
| - }
|
| - 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)
|
| @@ -482,215 +431,57 @@ TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCertificateChainBio)(TCN_STDARGS, jl
|
| return JNI_FALSE;
|
| }
|
|
|
| -TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCACertificate)(TCN_STDARGS,
|
| - jlong ctx,
|
| - jstring file,
|
| - jstring path)
|
| +TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCACertificateBio)(TCN_STDARGS, jlong ctx, jlong certs)
|
| {
|
| 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;
|
| -}
|
| + BIO *b = J2P(certs, BIO *);
|
|
|
| -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;
|
| - }
|
| + TCN_ASSERT(c != NULL);
|
|
|
| - 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);
|
| + return b != NULL && c->mode != SSL_MODE_CLIENT && SSL_CTX_use_client_CA_bio(c->ctx, b) > 0 ? JNI_TRUE : JNI_FALSE;
|
| }
|
|
|
| -TCN_IMPLEMENT_CALL(void, SSLContext, setTmpECDHByCurveName)(TCN_STDARGS, jlong ctx,
|
| - jstring curveName)
|
| +TCN_IMPLEMENT_CALL(void, SSLContext, setTmpDHLength)(TCN_STDARGS, jlong ctx, jint length)
|
| {
|
| -#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;
|
| + switch (length) {
|
| + case 512:
|
| + SSL_CTX_set_tmp_dh_callback(c->ctx, SSL_callback_tmp_DH_512);
|
| + return;
|
| + case 1024:
|
| + SSL_CTX_set_tmp_dh_callback(c->ctx, SSL_callback_tmp_DH_1024);
|
| + return;
|
| + case 2048:
|
| + SSL_CTX_set_tmp_dh_callback(c->ctx, SSL_callback_tmp_DH_2048);
|
| + return;
|
| + case 4096:
|
| + SSL_CTX_set_tmp_dh_callback(c->ctx, SSL_callback_tmp_DH_4096);
|
| + return;
|
| + default:
|
| + tcn_Throw(e, "Unsupported length %s", length);
|
| + 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_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 */
|
| - }
|
| - }
|
| + TCN_ASSERT(c != NULL);
|
|
|
| - SSL_CTX_set_verify(c->ctx, verify, SSL_callback_SSL_verify);
|
| + // No need to set the callback for SSL_CTX_set_verify because we override the default certificate verification via SSL_CTX_set_cert_verify_callback.
|
| + SSL_CTX_set_verify(c->ctx, tcn_set_verify_config(&c->verify_config, level, depth), NULL);
|
| + SSL_CTX_set_verify_depth(c->ctx, c->verify_config.verify_depth);
|
| }
|
|
|
| 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;
|
| @@ -699,38 +490,10 @@ static EVP_PKEY *load_pem_key(tcn_ssl_ctxt_t *c, const char *file)
|
| 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 != NULL)
|
| - break;
|
| - cb_data->password[0] = '\0';
|
| - BIO_ctrl(bio, BIO_CTRL_RESET, 0, NULL);
|
| - }
|
| - BIO_free(bio);
|
| - return key;
|
| -}
|
|
|
| -static EVP_PKEY *load_pem_key_bio(tcn_ssl_ctxt_t *c, const BIO *bio)
|
| -{
|
| - EVP_PKEY *key = NULL;
|
| - tcn_pass_cb_t *cb_data = c->cb_data;
|
| - int i;
|
| + key = PEM_read_bio_PrivateKey(bio, NULL, (pem_password_cb *)SSL_password_callback, (void *)c->password);
|
|
|
| - if (cb_data == NULL)
|
| - cb_data = &tcn_password_callback;
|
| - for (i = 0; i < 3; i++) {
|
| - key = PEM_read_bio_PrivateKey((BIO*) bio, NULL,
|
| - (pem_password_cb *)SSL_password_callback,
|
| - (void *)cb_data);
|
| - if (key)
|
| - break;
|
| - cb_data->password[0] = '\0';
|
| - BIO_ctrl((BIO*) bio, BIO_CTRL_RESET, 0, NULL);
|
| - }
|
| + BIO_free(bio);
|
| return key;
|
| }
|
|
|
| @@ -738,7 +501,6 @@ 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;
|
| @@ -747,11 +509,9 @@ static X509 *load_pem_cert(tcn_ssl_ctxt_t *c, const char *file)
|
| 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);
|
| + (void *)c->password);
|
| if (cert == NULL &&
|
| (ERR_GET_REASON(ERR_peek_last_error()) == PEM_R_NO_START_LINE)) {
|
| ERR_clear_error();
|
| @@ -762,25 +522,6 @@ static X509 *load_pem_cert(tcn_ssl_ctxt_t *c, const char *file)
|
| return cert;
|
| }
|
|
|
| -static X509 *load_pem_cert_bio(tcn_ssl_ctxt_t *c, const BIO *bio)
|
| -{
|
| - X509 *cert = NULL;
|
| - tcn_pass_cb_t *cb_data = c->cb_data;
|
| -
|
| - if (cb_data == NULL)
|
| - cb_data = &tcn_password_callback;
|
| - cert = PEM_read_bio_X509_AUX((BIO*) 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, BIO_CTRL_RESET, 0, NULL);
|
| - cert = d2i_X509_bio((BIO*) bio, NULL);
|
| - }
|
| - return cert;
|
| -}
|
| -
|
| static int ssl_load_pkcs12(tcn_ssl_ctxt_t *c, const char *file,
|
| EVP_PKEY **pkey, X509 **cert, STACK_OF(X509) **ca)
|
| {
|
| @@ -789,7 +530,6 @@ static int ssl_load_pkcs12(tcn_ssl_ctxt_t *c, const char *file,
|
| 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;
|
| @@ -807,9 +547,7 @@ static int ssl_load_pkcs12(tcn_ssl_ctxt_t *c, const char *file,
|
| pass = "";
|
| }
|
| else {
|
| - if (!cb_data)
|
| - cb_data = &tcn_password_callback;
|
| - len = SSL_password_callback(buff, PEM_BUFSIZE, 0, cb_data);
|
| + len = SSL_password_callback(buff, PEM_BUFSIZE, 0, (void *) c->password);
|
| if (len < 0) {
|
| /* Passpharse callback error */
|
| goto cleanup;
|
| @@ -828,45 +566,47 @@ cleanup:
|
| 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);
|
| +static void free_and_reset_pass(tcn_ssl_ctxt_t *c, char* old_password, const jboolean rv) {
|
| + if (!rv) {
|
| + if (c->password != NULL) {
|
| + free(c->password);
|
| + c->password = NULL;
|
| + }
|
| + // Restore old password
|
| + c->password = old_password;
|
| + } else if (old_password != NULL) {
|
| + free(old_password);
|
| + }
|
| }
|
|
|
| +
|
| TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCertificate)(TCN_STDARGS, jlong ctx,
|
| jstring cert, jstring key,
|
| - jstring password, jint idx)
|
| + jstring password)
|
| {
|
| 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);
|
| + EVP_PKEY *pkey = NULL;
|
| + X509 *xcert = NULL;
|
| const char *key_file, *cert_file;
|
| const char *p;
|
| + char *old_password = NULL;
|
| char err[256];
|
|
|
| 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';
|
| + old_password = c->password;
|
| +
|
| + c->password = strdup(cpassword);
|
| + if (c->password == NULL) {
|
| + rv = JNI_FALSE;
|
| + goto cleanup;
|
| + }
|
| }
|
| key_file = J2S(key);
|
| cert_file = J2S(cert);
|
| @@ -878,7 +618,7 @@ TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCertificate)(TCN_STDARGS, jlong ctx,
|
| 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)) {
|
| + if (!ssl_load_pkcs12(c, cert_file, &pkey, &xcert, 0)) {
|
| ERR_error_string(ERR_get_error(), err);
|
| tcn_Throw(e, "Unable to load certificate %s (%s)",
|
| cert_file, err);
|
| @@ -887,14 +627,14 @@ TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCertificate)(TCN_STDARGS, jlong ctx,
|
| }
|
| }
|
| else {
|
| - if ((c->keys[idx] = load_pem_key(c, key_file)) == NULL) {
|
| + if ((pkey = 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) {
|
| + if ((xcert = 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);
|
| @@ -902,13 +642,13 @@ TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCertificate)(TCN_STDARGS, jlong ctx,
|
| goto cleanup;
|
| }
|
| }
|
| - if (SSL_CTX_use_certificate(c->ctx, c->certs[idx]) <= 0) {
|
| + if (SSL_CTX_use_certificate(c->ctx, xcert) <= 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) {
|
| + if (SSL_CTX_use_PrivateKey(c->ctx, pkey) <= 0) {
|
| ERR_error_string(ERR_get_error(), err);
|
| tcn_Throw(e, "Error setting private key (%s)", err);
|
| rv = JNI_FALSE;
|
| @@ -925,35 +665,40 @@ cleanup:
|
| TCN_FREE_CSTRING(cert);
|
| TCN_FREE_CSTRING(key);
|
| TCN_FREE_CSTRING(password);
|
| + EVP_PKEY_free(pkey); // this function is safe to call with NULL
|
| + X509_free(xcert); // this function is safe to call with NULL
|
| + free_and_reset_pass(c, old_password, rv);
|
| return rv;
|
| }
|
|
|
| TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCertificateBio)(TCN_STDARGS, jlong ctx,
|
| jlong cert, jlong key,
|
| - jstring password, jint idx)
|
| + jstring password)
|
| {
|
| tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
|
| BIO *cert_bio = J2P(cert, BIO *);
|
| BIO *key_bio = J2P(key, BIO *);
|
| + EVP_PKEY *pkey = NULL;
|
| + X509 *xcert = NULL;
|
|
|
| jboolean rv = JNI_TRUE;
|
| TCN_ALLOC_CSTRING(password);
|
| + char *old_password = NULL;
|
| char err[256];
|
|
|
| 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';
|
| + old_password = c->password;
|
| +
|
| + c->password = strdup(cpassword);
|
| + if (c->password == NULL) {
|
| + rv = JNI_FALSE;
|
| + goto cleanup;
|
| + }
|
| }
|
| +
|
| if (!key)
|
| key = cert;
|
| if (!cert || !key) {
|
| @@ -962,14 +707,14 @@ TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCertificateBio)(TCN_STDARGS, jlong c
|
| goto cleanup;
|
| }
|
|
|
| - if ((c->keys[idx] = load_pem_key_bio(c, key_bio)) == NULL) {
|
| + if ((pkey = load_pem_key_bio(c->password, key_bio)) == NULL) {
|
| ERR_error_string(ERR_get_error(), err);
|
| ERR_clear_error();
|
| tcn_Throw(e, "Unable to load certificate key (%s)",err);
|
| rv = JNI_FALSE;
|
| goto cleanup;
|
| }
|
| - if ((c->certs[idx] = load_pem_cert_bio(c, cert_bio)) == NULL) {
|
| + if ((xcert = load_pem_cert_bio(c->password, cert_bio)) == NULL) {
|
| ERR_error_string(ERR_get_error(), err);
|
| ERR_clear_error();
|
| tcn_Throw(e, "Unable to load certificate (%s) ", err);
|
| @@ -977,14 +722,14 @@ TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCertificateBio)(TCN_STDARGS, jlong c
|
| goto cleanup;
|
| }
|
|
|
| - if (SSL_CTX_use_certificate(c->ctx, c->certs[idx]) <= 0) {
|
| + if (SSL_CTX_use_certificate(c->ctx, xcert) <= 0) {
|
| ERR_error_string(ERR_get_error(), err);
|
| ERR_clear_error();
|
| tcn_Throw(e, "Error setting certificate (%s)", err);
|
| rv = JNI_FALSE;
|
| goto cleanup;
|
| }
|
| - if (SSL_CTX_use_PrivateKey(c->ctx, c->keys[idx]) <= 0) {
|
| + if (SSL_CTX_use_PrivateKey(c->ctx, pkey) <= 0) {
|
| ERR_error_string(ERR_get_error(), err);
|
| ERR_clear_error();
|
| tcn_Throw(e, "Error setting private key (%s)", err);
|
| @@ -1002,17 +747,19 @@ TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCertificateBio)(TCN_STDARGS, jlong c
|
| }
|
| cleanup:
|
| TCN_FREE_CSTRING(password);
|
| + EVP_PKEY_free(pkey); // this function is safe to call with NULL
|
| + X509_free(xcert); // this function is safe to call with NULL
|
| + free_and_reset_pass(c, old_password, rv);
|
| return rv;
|
| }
|
|
|
| -
|
| // Convert protos to wire format
|
| static int initProtocols(JNIEnv *e, 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.
|
| + // We will call realloc to increase this if needed.
|
| size_t p_data_size = 128;
|
| size_t p_data_len = 0;
|
| jstring proto_string;
|
| @@ -1273,6 +1020,34 @@ TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionCacheFull)(TCN_STDARGS, jlong ctx)
|
| return rv;
|
| }
|
|
|
| +TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionTicketKeyNew)(TCN_STDARGS, jlong ctx)
|
| +{
|
| + tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
|
| + jlong rv = apr_atomic_read32(&c->ticket_keys_new);
|
| + return rv;
|
| +}
|
| +
|
| +TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionTicketKeyResume)(TCN_STDARGS, jlong ctx)
|
| +{
|
| + tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
|
| + jlong rv = apr_atomic_read32(&c->ticket_keys_resume);
|
| + return rv;
|
| +}
|
| +
|
| +TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionTicketKeyRenew)(TCN_STDARGS, jlong ctx)
|
| +{
|
| + tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
|
| + jlong rv = apr_atomic_read32(&c->ticket_keys_renew);
|
| + return rv;
|
| +}
|
| +
|
| +TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionTicketKeyFail)(TCN_STDARGS, jlong ctx)
|
| +{
|
| + tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
|
| + jlong rv = apr_atomic_read32(&c->ticket_keys_fail);
|
| + return rv;
|
| +}
|
| +
|
| static int current_session_key(tcn_ssl_ctxt_t *c, tcn_ssl_ticket_key_t *key) {
|
| int result = JNI_FALSE;
|
| apr_thread_rwlock_rdlock(c->mutex);
|
| @@ -1303,37 +1078,44 @@ static int find_session_key(tcn_ssl_ctxt_t *c, unsigned char key_name[16], tcn_s
|
| }
|
|
|
| static int ssl_tlsext_ticket_key_cb(SSL *s, unsigned char key_name[16], unsigned char *iv, EVP_CIPHER_CTX *ctx, HMAC_CTX *hctx, int enc) {
|
| - tcn_ssl_ctxt_t *c = SSL_get_app_data2(s);
|
| - tcn_ssl_ticket_key_t key;
|
| - int is_current_key;
|
| -
|
| - if (enc) { /* create new session */
|
| - if (current_session_key(c, &key)) {
|
| - if (RAND_bytes(iv, EVP_MAX_IV_LENGTH) <= 0) {
|
| - return -1; /* insufficient random */
|
| - }
|
| -
|
| - memcpy(key_name, key.key_name, 16);
|
| -
|
| - EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key.aes_key, iv);
|
| - HMAC_Init_ex(hctx, key.hmac_key, 16, EVP_sha256(), NULL);
|
| - return 1;
|
| - }
|
| - // No ticket configured
|
| - return 0;
|
| - } else { /* retrieve session */
|
| - if (find_session_key(c, key_name, &key, &is_current_key)) {
|
| - HMAC_Init_ex(hctx, key.hmac_key, 16, EVP_sha256(), NULL);
|
| - EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key.aes_key, iv );
|
| - if (!is_current_key) {
|
| - return 2;
|
| - }
|
| - return 1;
|
| - }
|
| - // No ticket
|
| - return 0;
|
| - }
|
| -}
|
| + tcn_ssl_ctxt_t *c = SSL_get_app_data2(s);
|
| + tcn_ssl_ticket_key_t key;
|
| + int is_current_key;
|
| +
|
| + if (enc) { /* create new session */
|
| + if (current_session_key(c, &key)) {
|
| + if (RAND_bytes(iv, EVP_MAX_IV_LENGTH) <= 0) {
|
| + return -1; /* insufficient random */
|
| + }
|
| +
|
| + memcpy(key_name, key.key_name, 16);
|
| +
|
| + EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key.aes_key, iv);
|
| + HMAC_Init_ex(hctx, key.hmac_key, 16, EVP_sha256(), NULL);
|
| + apr_atomic_inc32(&c->ticket_keys_new);
|
| + return 1;
|
| + }
|
| + // No ticket configured
|
| + return 0;
|
| + } else { /* retrieve session */
|
| + if (find_session_key(c, key_name, &key, &is_current_key)) {
|
| + HMAC_Init_ex(hctx, key.hmac_key, 16, EVP_sha256(), NULL);
|
| + EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key.aes_key, iv );
|
| + if (!is_current_key) {
|
| + // The ticket matched a key in the list, and we want to upgrade it to the current
|
| + // key.
|
| + apr_atomic_inc32(&c->ticket_keys_renew);
|
| + return 2;
|
| + }
|
| + // The ticket matched the current key.
|
| + apr_atomic_inc32(&c->ticket_keys_resume);
|
| + return 1;
|
| + }
|
| + // No matching ticket.
|
| + apr_atomic_inc32(&c->ticket_keys_fail);
|
| + return 0;
|
| + }
|
| +}
|
|
|
| TCN_IMPLEMENT_CALL(void, SSLContext, setSessionTicketKeys0)(TCN_STDARGS, jlong ctx, jbyteArray keys)
|
| {
|
| @@ -1369,104 +1151,21 @@ TCN_IMPLEMENT_CALL(void, SSLContext, setSessionTicketKeys0)(TCN_STDARGS, jlong c
|
| SSL_CTX_set_tlsext_ticket_key_cb(c->ctx, ssl_tlsext_ticket_key_cb);
|
| }
|
|
|
| -
|
| -/*
|
| - * Adapted from OpenSSL:
|
| - * http://osxr.org/openssl/source/ssl/ssl_locl.h#0291
|
| - */
|
| -/* Bits for algorithm_mkey (key exchange algorithm) */
|
| -#define SSL_kRSA 0x00000001L /* RSA key exchange */
|
| -#define SSL_kDHr 0x00000002L /* DH cert, RSA CA cert */ /* no such ciphersuites supported! */
|
| -#define SSL_kDHd 0x00000004L /* DH cert, DSA CA cert */ /* no such ciphersuite supported! */
|
| -#define SSL_kEDH 0x00000008L /* tmp DH key no DH cert */
|
| -#define SSL_kKRB5 0x00000010L /* Kerberos5 key exchange */
|
| -#define SSL_kECDHr 0x00000020L /* ECDH cert, RSA CA cert */
|
| -#define SSL_kECDHe 0x00000040L /* ECDH cert, ECDSA CA cert */
|
| -#define SSL_kEECDH 0x00000080L /* ephemeral ECDH */
|
| -#define SSL_kPSK 0x00000100L /* PSK */
|
| -#define SSL_kGOST 0x00000200L /* GOST key exchange */
|
| -#define SSL_kSRP 0x00000400L /* SRP */
|
| -
|
| -/* Bits for algorithm_auth (server authentication) */
|
| -#define SSL_aRSA 0x00000001L /* RSA auth */
|
| -#define SSL_aDSS 0x00000002L /* DSS auth */
|
| -#define SSL_aNULL 0x00000004L /* no auth (i.e. use ADH or AECDH) */
|
| -#define SSL_aDH 0x00000008L /* Fixed DH auth (kDHd or kDHr) */ /* no such ciphersuites supported! */
|
| -#define SSL_aECDH 0x00000010L /* Fixed ECDH auth (kECDHe or kECDHr) */
|
| -#define SSL_aKRB5 0x00000020L /* KRB5 auth */
|
| -#define SSL_aECDSA 0x00000040L /* ECDSA auth*/
|
| -#define SSL_aPSK 0x00000080L /* PSK auth */
|
| -#define SSL_aGOST94 0x00000100L /* GOST R 34.10-94 signature auth */
|
| -#define SSL_aGOST01 0x00000200L /* GOST R 34.10-2001 signature auth */
|
| -
|
| -/* OpenSSL end */
|
| -
|
| -/*
|
| - * Adapted from Android:
|
| - * https://android.googlesource.com/platform/external/openssl/+/master/patches/0003-jsse.patch
|
| - */
|
| -const char* cipher_authentication_method(const SSL_CIPHER* cipher){
|
| -#ifndef OPENSSL_IS_BORINGSSL
|
| - switch (cipher->algorithm_mkey)
|
| - {
|
| - case SSL_kRSA:
|
| - return SSL_TXT_RSA;
|
| - case SSL_kDHr:
|
| - return SSL_TXT_DH "_" SSL_TXT_RSA;
|
| -
|
| - case SSL_kDHd:
|
| - return SSL_TXT_DH "_" SSL_TXT_DSS;
|
| - case SSL_kEDH:
|
| - switch (cipher->algorithm_auth)
|
| - {
|
| - case SSL_aDSS:
|
| - return "DHE_" SSL_TXT_DSS;
|
| - case SSL_aRSA:
|
| - return "DHE_" SSL_TXT_RSA;
|
| - case SSL_aNULL:
|
| - return SSL_TXT_DH "_anon";
|
| - default:
|
| - return "UNKNOWN";
|
| - }
|
| - case SSL_kKRB5:
|
| - return SSL_TXT_KRB5;
|
| - case SSL_kECDHr:
|
| - return SSL_TXT_ECDH "_" SSL_TXT_RSA;
|
| - case SSL_kECDHe:
|
| - return SSL_TXT_ECDH "_" SSL_TXT_ECDSA;
|
| - case SSL_kEECDH:
|
| - switch (cipher->algorithm_auth)
|
| - {
|
| - case SSL_aECDSA:
|
| - return "ECDHE_" SSL_TXT_ECDSA;
|
| - case SSL_aRSA:
|
| - return "ECDHE_" SSL_TXT_RSA;
|
| - case SSL_aNULL:
|
| - return SSL_TXT_ECDH "_anon";
|
| - default:
|
| - return "UNKNOWN";
|
| - }
|
| - default:
|
| - return "UNKNOWN";
|
| - }
|
| -#else
|
| - return SSL_CIPHER_get_kx_name(cipher);
|
| -#endif
|
| -
|
| -}
|
| -
|
| static const char* authentication_method(const SSL* ssl) {
|
| {
|
| + const STACK_OF(SSL_CIPHER) *ciphers = NULL;
|
| +
|
| switch (SSL_version(ssl))
|
| {
|
| case SSL2_VERSION:
|
| return SSL_TXT_RSA;
|
| default:
|
| -#if defined(OPENSSL_IS_BORINGSSL)
|
| - return cipher_authentication_method(SSL_get_pending_cipher(ssl));
|
| -#else
|
| - return cipher_authentication_method(ssl->s3->tmp.new_cipher);
|
| -#endif
|
| + ciphers = SSL_get_ciphers(ssl);
|
| + if (ciphers == NULL || sk_SSL_CIPHER_num(ciphers) <= 0) {
|
| + // No cipher available so return UNKNOWN.
|
| + return TCN_UNKNOWN_AUTH_METHOD;
|
| + }
|
| + return SSL_cipher_authentication_method(sk_SSL_CIPHER_value(ciphers, 0));
|
| }
|
| }
|
| }
|
| @@ -1475,13 +1174,22 @@ static const char* authentication_method(const SSL* ssl) {
|
| 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_ASSERT(ssl != NULL);
|
| tcn_ssl_ctxt_t *c = SSL_get_app_data2(ssl);
|
| -
|
| + TCN_ASSERT(c != NULL);
|
| + tcn_ssl_verify_config_t* verify_config = SSL_get_app_data4(ssl);
|
| + TCN_ASSERT(verify_config != NULL);
|
|
|
| // Get a stack of all certs in the chain
|
| STACK_OF(X509) *sk = ctx->untrusted;
|
|
|
| - int len = sk_num((_STACK*) sk);
|
| + // SSL_CTX_set_verify_depth() and SSL_set_verify_depth() set the limit up to which depth certificates in a chain are
|
| + // used during the verification procedure. If the certificate chain is longer than allowed, the certificates above
|
| + // the limit are ignored. Error messages are generated as if these certificates would not be present,
|
| + // most likely a X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY will be issued.
|
| + // https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_verify.html
|
| + const int totalQueuedLength = sk_X509_num(sk);
|
| + int len = TCN_MIN(verify_config->verify_depth, totalQueuedLength);
|
| unsigned i;
|
| X509 *cert;
|
| int length;
|
| @@ -1491,23 +1199,25 @@ static int SSL_cert_verify(X509_STORE_CTX *ctx, void *arg) {
|
| jbyteArray bArray;
|
| const char *authMethod;
|
| jstring authMethodString;
|
| - jboolean result;
|
| - int r;
|
| + jint result;
|
| + jclass byteArrayClass = tcn_get_byte_array_class();
|
| 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_value((_STACK*) sk, i);
|
| + cert = 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);
|
| + if (buf != 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);
|
| @@ -1523,17 +1233,35 @@ static int SSL_cert_verify(X509_STORE_CTX *ctx, void *arg) {
|
| authMethod = authentication_method(ssl);
|
| authMethodString = (*e)->NewStringUTF(e, authMethod);
|
|
|
| - result = (*e)->CallBooleanMethod(e, c->verifier, c->verifier_method, P2J(ssl), array,
|
| - authMethodString);
|
| + result = (*e)->CallIntMethod(e, c->verifier, c->verifier_method, P2J(ssl), array, authMethodString);
|
| +
|
| +#ifdef X509_V_ERR_UNSPECIFIED
|
| + // If we failed to verify for an unknown reason (currently this happens if we can't find a common root) then we should
|
| + // fail with the same status as recommended in the OpenSSL docs https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_verify.html
|
| + if (result == X509_V_ERR_UNSPECIFIED && len < totalQueuedLength) {
|
| + result = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY;
|
| + }
|
| +#else
|
| + // HACK!
|
| + // LibreSSL 2.4.x doesn't support the X509_V_ERR_UNSPECIFIED so we introduce a work around to make sure a supported alert is used.
|
| + // This should be reverted when we support LibreSSL 2.5.x (which does support X509_V_ERR_UNSPECIFIED).
|
| + if (result == TCN_X509_V_ERR_UNSPECIFIED) {
|
| + result = len < totalQueuedLength ? X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY : X509_V_ERR_CERT_REJECTED;
|
| + }
|
| +#endif
|
| +
|
| + // TODO(scott): if verify_config->verify_depth == SSL_CVERIFY_OPTIONAL we have the option to let the handshake
|
| + // succeed for some of the "informational" error messages (e.g. X509_V_ERR_EMAIL_MISMATCH ?)
|
|
|
| - r = result == JNI_TRUE ? 1 : 0;
|
| + // Set the correct error so it will be included in the alert.
|
| + X509_STORE_CTX_set_error(ctx, result);
|
|
|
| // 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;
|
| -}
|
|
|
| + return result == X509_V_OK ? 1 : 0;
|
| +}
|
|
|
| TCN_IMPLEMENT_CALL(void, SSLContext, setCertVerifyCallback)(TCN_STDARGS, jlong ctx, jobject verifier)
|
| {
|
| @@ -1546,7 +1274,7 @@ TCN_IMPLEMENT_CALL(void, SSLContext, setCertVerifyCallback)(TCN_STDARGS, jlong c
|
| 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");
|
| + jmethodID method = (*e)->GetMethodID(e, verifier_class, "verify", "(J[[BLjava/lang/String;)I");
|
|
|
| if (method == NULL) {
|
| return;
|
| @@ -1562,366 +1290,236 @@ TCN_IMPLEMENT_CALL(void, SSLContext, setCertVerifyCallback)(TCN_STDARGS, jlong c
|
| }
|
| }
|
|
|
| -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);
|
| +/**
|
| + * Returns an array containing all the X500 principal's bytes.
|
| + *
|
| + * Partly based on code from conscrypt:
|
| + * https://android.googlesource.com/platform/external/conscrypt/+/master/src/main/native/org_conscrypt_NativeCrypto.cpp
|
| + */
|
| +static jobjectArray principalBytes(JNIEnv* e, const STACK_OF(X509_NAME)* names) {
|
| + jobjectArray array;
|
| + jbyteArray bArray;
|
| + int i;
|
| + int count;
|
| + int length;
|
| unsigned char *buf;
|
| - int res;
|
| + X509_NAME* principal;
|
| + jclass byteArrayClass = tcn_get_byte_array_class();
|
|
|
| - 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;
|
| + if (names == NULL) {
|
| + return NULL;
|
| }
|
| - return JNI_FALSE;
|
| -}
|
| -#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);
|
| -}
|
| + count = sk_X509_NAME_num(names);
|
| + if (count <= 0) {
|
| + return NULL;
|
| + }
|
|
|
| -TCN_IMPLEMENT_CALL(jint, SSLContext, getOptions)(TCN_STDARGS, jlong ctx)
|
| -{
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| -}
|
| + array = (*e)->NewObjectArray(e, count, byteArrayClass, NULL);
|
| + if (array == NULL) {
|
| + return NULL;
|
| + }
|
|
|
| -TCN_IMPLEMENT_CALL(void, SSLContext, clearOptions)(TCN_STDARGS, jlong ctx,
|
| - jint opt)
|
| -{
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| - UNREFERENCED(opt);
|
| -}
|
| + for (i = 0; i < count; i++) {
|
| + principal = sk_X509_NAME_value(names, i);
|
| + buf = NULL;
|
| + length = i2d_X509_NAME(principal, &buf);
|
| + if (length < 0) {
|
| + if (buf != NULL) {
|
| + // We need to delete the local references so we not leak memory as this method is called via callback.
|
| + OPENSSL_free(buf);
|
| + }
|
| + // In case of error just return an empty byte[][]
|
| + return (*e)->NewObjectArray(e, 0, byteArrayClass, NULL);
|
| + }
|
| + bArray = (*e)->NewByteArray(e, length);
|
| + (*e)->SetByteArrayRegion(e, bArray, 0, length, (jbyte*) buf);
|
| + (*e)->SetObjectArrayElement(e, array, i, bArray);
|
|
|
| -TCN_IMPLEMENT_CALL(void, SSLContext, setQuietShutdown)(TCN_STDARGS, jlong ctx,
|
| - jboolean mode)
|
| -{
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| - UNREFERENCED(mode);
|
| -}
|
| + // 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);
|
| + }
|
|
|
| -TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCipherSuite)(TCN_STDARGS, jlong ctx,
|
| - jstring ciphers)
|
| -{
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| - UNREFERENCED(ciphers);
|
| - return JNI_FALSE;
|
| + return array;
|
| }
|
|
|
| -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;
|
| -}
|
| +static int cert_requested(SSL* ssl, X509** x509Out, EVP_PKEY** pkeyOut) {
|
| +#if defined(LIBRESSL_VERSION_NUMBER)
|
| + return -1;
|
| +#else
|
| + tcn_ssl_ctxt_t *c = SSL_get_app_data2(ssl);
|
| + int ctype_num;
|
| + jbyte* ctype_bytes;
|
| + jobjectArray issuers;
|
| + JNIEnv *e;
|
| + jbyteArray keyTypes;
|
| + jobject keyMaterial;
|
| + STACK_OF(X509) *chain = NULL;
|
| + X509 *cert = NULL;
|
| + EVP_PKEY* pkey = NULL;
|
| + jlong certChain;
|
| + jlong privateKey;
|
| + int certChainLen;
|
| + int i;
|
|
|
| -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_get_java_env(&e);
|
|
|
| -TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCertificateChainBio)(TCN_STDARGS, jlong ctx,
|
| - jlong chain,
|
| - jboolean skipfirst)
|
| -{
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| - UNREFERENCED(chain);
|
| - UNREFERENCED(skipfirst);
|
| - return JNI_FALSE;
|
| -}
|
| +#if OPENSSL_VERSION_NUMBER < 0x10002000L
|
| + char ssl2_ctype = SSL3_CT_RSA_SIGN;
|
| + switch (ssl->version) {
|
| + case SSL2_VERSION:
|
| + ctype_bytes = (jbyte*) &ssl2_ctype;
|
| + ctype_num = 1;
|
| + break;
|
| + case SSL3_VERSION:
|
| + case TLS1_VERSION:
|
| + case TLS1_1_VERSION:
|
| + case TLS1_2_VERSION:
|
| + case DTLS1_VERSION:
|
| + ctype_bytes = (jbyte*) ssl->s3->tmp.ctype;
|
| + ctype_num = ssl->s3->tmp.ctype_num;
|
| + break;
|
| + }
|
| +#else
|
| + ctype_num = SSL_get0_certificate_types(ssl, (const uint8_t **) &ctype_bytes);
|
| +#endif
|
| + if (ctype_num <= 0) {
|
| + // Use no certificate
|
| + return 0;
|
| + }
|
| + keyTypes = (*e)->NewByteArray(e, ctype_num);
|
| + if (keyTypes == NULL) {
|
| + // Something went seriously wrong, bail out!
|
| + return -1;
|
| + }
|
| + (*e)->SetByteArrayRegion(e, keyTypes, 0, ctype_num, ctype_bytes);
|
|
|
| -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;
|
| -}
|
| + issuers = principalBytes(e, SSL_get_client_CA_list(ssl));
|
|
|
| -TCN_IMPLEMENT_CALL(void, SSLContext, setShutdownType)(TCN_STDARGS, jlong ctx,
|
| - jint type)
|
| -{
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| - UNREFERENCED(type);
|
| -}
|
| + // Execute the java callback
|
| + keyMaterial = (*e)->CallObjectMethod(e, c->cert_requested_callback, c->cert_requested_callback_method, P2J(ssl), keyTypes, issuers);
|
| + if (keyMaterial == NULL) {
|
| + return 0;
|
| + }
|
|
|
| -TCN_IMPLEMENT_CALL(void, SSLContext, setVerify)(TCN_STDARGS, jlong ctx,
|
| - jint level, jint depth)
|
| -{
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| - UNREFERENCED(level);
|
| - UNREFERENCED(depth);
|
| -}
|
| + // Any failure after this line must cause a goto fail to cleanup things.
|
| + certChain = (*e)->GetLongField(e, keyMaterial, tcn_get_key_material_certificate_chain_field());
|
| + privateKey = (*e)->GetLongField(e, keyMaterial, tcn_get_key_material_private_key_field());
|
|
|
| -TCN_IMPLEMENT_CALL(void, SSLContext, setRandom)(TCN_STDARGS, jlong ctx,
|
| - jstring file)
|
| -{
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| - UNREFERENCED(file);
|
| -}
|
| + chain = J2P(certChain, STACK_OF(X509) *);
|
| + pkey = J2P(privateKey, EVP_PKEY *);
|
|
|
| -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, setCertificateBio)(TCN_STDARGS, jlong ctx,
|
| - jlong cert, jlong key,
|
| - jstring password, jint idx)
|
| -{
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| - UNREFERENCED(cert);
|
| - UNREFERENCED(key);
|
| - UNREFERENCED(password);
|
| - UNREFERENCED(idx);
|
| - return JNI_FALSE;
|
| -}
|
| -TCN_IMPLEMENT_CALL(void, SSLContext, setNpnProtos)(TCN_STDARGS, jlong ctx, jobjectArray next_protos,
|
| - jint selectorFailureBehavior)
|
| -{
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| - UNREFERENCED(next_protos);
|
| -}
|
| + if (chain == NULL || pkey == NULL) {
|
| + goto fail;
|
| + }
|
|
|
| + certChainLen = sk_X509_num(chain);
|
|
|
| -TCN_IMPLEMENT_CALL(void, SSLContext, setAlpnProtos)(TCN_STDARGS, jlong ctx, jobjectArray alpn_protos,
|
| - jint selectorFailureBehavior)
|
| -{
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| - UNREFERENCED(alpn_protos);
|
| -}
|
| + if (certChainLen <= 0) {
|
| + goto fail;
|
| + }
|
|
|
| -TCN_IMPLEMENT_CALL(jlong, SSLContext, setSessionCacheMode)(TCN_STDARGS, jlong ctx, jlong mode)
|
| -{
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| - UNREFERENCED(mode);
|
| - return -1;
|
| -}
|
| + // Skip the first cert in the chain as we will write this to x509Out.
|
| + // See https://github.com/netty/netty-tcnative/issues/184
|
| + for (i = 1; i < certChainLen; ++i) {
|
| + // We need to explicit add extra certs to the chain as stated in:
|
| + // https://www.openssl.org/docs/manmaster/ssl/SSL_CTX_set_client_cert_cb.html
|
| + //
|
| + // Using SSL_add0_chain_cert(...) here as we not want to increment the reference count.
|
| + if (SSL_add0_chain_cert(ssl, sk_X509_value(chain, i)) <= 0) {
|
| + goto fail;
|
| + }
|
| + }
|
|
|
| -TCN_IMPLEMENT_CALL(jlong, SSLContext, getSessionCacheMode)(TCN_STDARGS, jlong ctx)
|
| -{
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| - return -1;
|
| -}
|
| + cert = sk_X509_value(chain, 0);
|
| + // Increment the reference count as we already set the chain via SSL_set0_chain(...) and using a cert out of it.
|
| + if (tcn_X509_up_ref(cert) <= 0) {
|
| + goto fail;
|
| + }
|
| + *x509Out = cert;
|
| + *pkeyOut = pkey;
|
|
|
| + // Free the stack it self but not the certs.
|
| + sk_X509_free(chain);
|
| + return 1;
|
| +fail:
|
| + ERR_clear_error();
|
| + sk_X509_pop_free(chain, X509_free);
|
| + EVP_PKEY_free(pkey);
|
|
|
| -TCN_IMPLEMENT_CALL(jlong, SSLContext, setSessionCacheTimeout)(TCN_STDARGS, jlong ctx, jlong timeout)
|
| -{
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| - UNREFERENCED(timeout);
|
| + // TODO: Would it be more correct to return 0 in this case we may not want to use any cert / private key ?
|
| return -1;
|
| +#endif /* defined(LIBRESSL_VERSION_NUMBER) */
|
| }
|
|
|
| -TCN_IMPLEMENT_CALL(jlong, SSLContext, getSessionCacheTimeout)(TCN_STDARGS, jlong ctx)
|
| +TCN_IMPLEMENT_CALL(void, SSLContext, setCertRequestedCallback)(TCN_STDARGS, jlong ctx, jobject callback)
|
| {
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| - return -1;
|
| -}
|
| + tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
|
|
|
| -TCN_IMPLEMENT_CALL(jlong, SSLContext, setSessionCacheSize)(TCN_STDARGS, jlong ctx, jlong size)
|
| -{
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| - UNREFERENCED(size);
|
| - return -1;
|
| -}
|
| + UNREFERENCED(o);
|
| + TCN_ASSERT(ctx != 0);
|
|
|
| -TCN_IMPLEMENT_CALL(jlong, SSLContext, getSessionCacheSize)(TCN_STDARGS, jlong ctx)
|
| -{
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| - return -1;
|
| -}
|
| + if (callback == NULL) {
|
| + SSL_CTX_set_client_cert_cb(c->ctx, NULL);
|
| + } else {
|
| + jclass callback_class = (*e)->GetObjectClass(e, callback);
|
| + jmethodID method = (*e)->GetMethodID(e, callback_class, "requested", "(J[B[[B)Lio/netty/internal/tcnative/CertificateRequestedCallback$KeyMaterial;");
|
| + if (method == NULL) {
|
| + return;
|
| + }
|
| + // Delete the reference to the previous specified verifier if needed.
|
| + if (c->cert_requested_callback != NULL) {
|
| + (*e)->DeleteLocalRef(e, c->cert_requested_callback);
|
| + }
|
| + c->cert_requested_callback = (*e)->NewGlobalRef(e, callback);
|
| + c->cert_requested_callback_method = method;
|
|
|
| -TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionNumber)(TCN_STDARGS, jlong ctx)
|
| -{
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| - return 0;
|
| + SSL_CTX_set_client_cert_cb(c->ctx, cert_requested);
|
| + }
|
| }
|
|
|
| -TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionConnect)(TCN_STDARGS, jlong ctx)
|
| +TCN_IMPLEMENT_CALL(jboolean, SSLContext, setSessionIdContext)(TCN_STDARGS, jlong ctx, jbyteArray sidCtx)
|
| {
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| - return 0;
|
| -}
|
| + tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
|
| + int len = (*e)->GetArrayLength(e, sidCtx);
|
| + unsigned char *buf;
|
| + int res;
|
|
|
| -TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionConnectGood)(TCN_STDARGS, jlong ctx)
|
| -{
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| - return 0;
|
| -}
|
| + UNREFERENCED(o);
|
| + TCN_ASSERT(ctx != 0);
|
|
|
| -TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionConnectRenegotiate)(TCN_STDARGS, jlong ctx)
|
| -{
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| - return 0;
|
| -}
|
| + buf = malloc(len);
|
|
|
| -TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionAccept)(TCN_STDARGS, jlong ctx)
|
| -{
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| - return 0;
|
| -}
|
| + (*e)->GetByteArrayRegion(e, sidCtx, 0, len, (jbyte*) buf);
|
|
|
| -TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionAcceptGood)(TCN_STDARGS, jlong ctx)
|
| -{
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| - return 0;
|
| -}
|
| + res = SSL_CTX_set_session_id_context(c->ctx, buf, len);
|
| + free(buf);
|
|
|
| -TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionAcceptRenegotiate)(TCN_STDARGS, jlong ctx)
|
| -{
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| - return 0;
|
| + if (res == 1) {
|
| + return JNI_TRUE;
|
| + }
|
| + return JNI_FALSE;
|
| }
|
|
|
| -TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionHits)(TCN_STDARGS, jlong ctx)
|
| +TCN_IMPLEMENT_CALL(jint, SSLContext, setMode)(TCN_STDARGS, jlong ctx, jint mode)
|
| {
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| - return 0;
|
| -}
|
| + tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
|
|
|
| -TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionCbHits)(TCN_STDARGS, jlong ctx)
|
| -{
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| - return 0;
|
| -}
|
| + UNREFERENCED(o);
|
| + TCN_ASSERT(ctx != 0);
|
|
|
| -TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionTimeouts)(TCN_STDARGS, jlong ctx)
|
| -{
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| - return 0;
|
| + return (jint) SSL_CTX_set_mode(c->ctx, mode);
|
| }
|
|
|
| -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)
|
| +TCN_IMPLEMENT_CALL(jint, SSLContext, getMode)(TCN_STDARGS, jlong ctx)
|
| {
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| - return 0;
|
| -}
|
| + tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
|
|
|
| -TCN_IMPLEMENT_CALL(void, SSLContext, setSessionTicketKeys0)(TCN_STDARGS, jlong ctx, jbyteArray keys)
|
| -{
|
| - UNREFERENCED_STDARGS;
|
| - UNREFERENCED(ctx);
|
| - UNREFERENCED(keys);
|
| -}
|
| + UNREFERENCED(o);
|
| + TCN_ASSERT(ctx != 0);
|
|
|
| -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;
|
| + return (jint) SSL_CTX_get_mode(c->ctx);
|
| }
|
| -#endif
|
|
|