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 |