Index: c/ssl.c |
diff --git a/c/ssl.c b/c/ssl.c |
index f8302d43a46e733a518d1fde35133b5d39a3cc6e..15891508348ad9d21b64db0416b4dd948d443fb9 100644 |
--- a/c/ssl.c |
+++ b/c/ssl.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,34 +29,25 @@ |
* limitations under the License. |
*/ |
-/* |
- * |
- * @author Mladen Turk |
- * @version $Id: ssl.c 1649733 2015-01-06 04:42:24Z billbarker $ |
- */ |
- |
+#include <stdbool.h> |
+#include <openssl/bio.h> |
#include "tcn.h" |
#include "apr_file_io.h" |
#include "apr_thread_mutex.h" |
#include "apr_atomic.h" |
-#include "apr_poll.h" |
-#ifdef HAVE_OPENSSL |
+#include "apr_strings.h" |
+#include "apr_portable.h" |
#include "ssl_private.h" |
static int ssl_initialized = 0; |
-static char *ssl_global_rand_file = NULL; |
extern apr_pool_t *tcn_global_pool; |
ENGINE *tcn_ssl_engine = NULL; |
void *SSL_temp_keys[SSL_TMP_KEY_MAX]; |
-tcn_pass_cb_t tcn_password_callback; |
/* Global reference to the pool used by the dynamic mutexes */ |
static apr_pool_t *dynlockpool = NULL; |
-static jclass byteArrayClass; |
-static jclass stringClass; |
- |
/* Dynamic lock structure */ |
struct CRYPTO_dynlock_value { |
apr_pool_t *pool; |
@@ -50,6 +56,16 @@ struct CRYPTO_dynlock_value { |
apr_thread_mutex_t *mutex; |
}; |
+struct TCN_bio_bytebuffer { |
+ // Pointer arithmetic is done on this variable. The type must correspond to a "byte" size. |
+ char* buffer; |
+ char* nonApplicationBuffer; |
+ jint nonApplicationBufferSize; |
+ jint nonApplicationBufferOffset; |
+ jint nonApplicationBufferLength; |
+ jint bufferLength; |
+ bool bufferIsSSLWriteSink; |
+}; |
/* |
* Handle the Temporary RSA Keys and DH Params |
@@ -67,17 +83,10 @@ struct CRYPTO_dynlock_value { |
SSL_TMP_KEY_FREE(type, SSL_TMP_KEY_##type##_2048); \ |
SSL_TMP_KEY_FREE(type, SSL_TMP_KEY_##type##_4096) |
-#define SSL_TMP_KEY_INIT_RSA(bits) \ |
- ssl_tmp_key_init_rsa(bits, SSL_TMP_KEY_RSA_##bits) |
- |
#define SSL_TMP_KEY_INIT_DH(bits) \ |
ssl_tmp_key_init_dh(bits, SSL_TMP_KEY_DH_##bits) |
#define SSL_TMP_KEYS_INIT(R) \ |
- SSL_temp_keys[SSL_TMP_KEY_RSA_2048] = NULL; \ |
- SSL_temp_keys[SSL_TMP_KEY_RSA_4096] = NULL; \ |
- R |= SSL_TMP_KEY_INIT_RSA(512); \ |
- R |= SSL_TMP_KEY_INIT_RSA(1024); \ |
R |= SSL_TMP_KEY_INIT_DH(512); \ |
R |= SSL_TMP_KEY_INIT_DH(1024); \ |
R |= SSL_TMP_KEY_INIT_DH(2048); \ |
@@ -120,10 +129,6 @@ static const jint supported_ssl_opts = 0 |
| SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS |
#endif |
-#ifdef SSL_OP_EPHEMERAL_RSA |
- | SSL_OP_EPHEMERAL_RSA |
-#endif |
- |
#ifdef SSL_OP_LEGACY_SERVER_CONNECT |
| SSL_OP_LEGACY_SERVER_CONNECT |
#endif |
@@ -229,66 +234,311 @@ static const jint supported_ssl_opts = 0 |
#endif |
| 0; |
-static int ssl_tmp_key_init_rsa(int bits, int idx) |
-{ |
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) |
- return 0; |
-#else |
+static jint tcn_flush_sslbuffer_to_bytebuffer(struct TCN_bio_bytebuffer* bioUserData) { |
+ jint writeAmount = TCN_MIN(bioUserData->bufferLength, bioUserData->nonApplicationBufferLength) * sizeof(char); |
+ jint writeChunk = bioUserData->nonApplicationBufferSize - bioUserData->nonApplicationBufferOffset; |
-#ifdef OPENSSL_FIPS |
- /** |
- * Short RSA keys cannot be generated in FIPS mode. |
- */ |
- if (bits < 1024) |
+#ifdef NETTY_TCNATIVE_BIO_DEBUG |
+ fprintf(stderr, "tcn_flush_sslbuffer_to_bytebuffer1 bioUserData->nonApplicationBufferLength %d bioUserData->nonApplicationBufferOffset %d writeChunk %d writeAmount %d\n", bioUserData->nonApplicationBufferLength, bioUserData->nonApplicationBufferOffset, writeChunk, writeAmount); |
+#endif |
+ |
+ // check if we need to account for wrap around when draining the internal SSL buffer. |
+ if (writeAmount > writeChunk) { |
+ jint newnonApplicationBufferOffset = writeAmount - writeChunk; |
+ memcpy(bioUserData->buffer, &bioUserData->nonApplicationBuffer[bioUserData->nonApplicationBufferOffset], (size_t) writeChunk); |
+ memcpy(&bioUserData->buffer[writeChunk], bioUserData->nonApplicationBuffer, (size_t) newnonApplicationBufferOffset); |
+ bioUserData->nonApplicationBufferOffset = newnonApplicationBufferOffset; |
+ } else { |
+ memcpy(bioUserData->buffer, &bioUserData->nonApplicationBuffer[bioUserData->nonApplicationBufferOffset], (size_t) writeAmount); |
+ bioUserData->nonApplicationBufferOffset += writeAmount; |
+ } |
+ bioUserData->nonApplicationBufferLength -= writeAmount; |
+ bioUserData->bufferLength -= writeAmount; |
+ bioUserData->buffer += writeAmount; // Pointer arithmetic based on char* type |
+ |
+ if (bioUserData->nonApplicationBufferLength == 0) { |
+ bioUserData->nonApplicationBufferOffset = 0; |
+ } |
+ |
+#ifdef NETTY_TCNATIVE_BIO_DEBUG |
+ fprintf(stderr, "tcn_flush_sslbuffer_to_bytebuffer2 bioUserData->nonApplicationBufferLength %d bioUserData->nonApplicationBufferOffset %d\n", bioUserData->nonApplicationBufferLength, bioUserData->nonApplicationBufferOffset); |
+#endif |
+ |
+ return writeAmount; |
+} |
+ |
+static jint tcn_write_to_bytebuffer(BIO* bio, const char* in, int inl) { |
+ jint writeAmount = 0; |
+ jint writeChunk; |
+ struct TCN_bio_bytebuffer* bioUserData = (struct TCN_bio_bytebuffer*) BIO_get_data(bio); |
+ TCN_ASSERT(bioUserData != NULL); |
+ |
+#ifdef NETTY_TCNATIVE_BIO_DEBUG |
+ fprintf(stderr, "tcn_write_to_bytebuffer bioUserData->bufferIsSSLWriteSink %d inl %d [%.*s]\n", bioUserData->bufferIsSSLWriteSink, inl, inl, in); |
+#endif |
+ |
+ if (in == NULL || inl <= 0) { |
+ return 0; |
+ } |
+ |
+ // If the buffer is currently being used for reading then we have to use the internal SSL buffer to queue the data. |
+ if (!bioUserData->bufferIsSSLWriteSink) { |
+ jint nonApplicationBufferFreeSpace = bioUserData->nonApplicationBufferSize - bioUserData->nonApplicationBufferLength; |
+ jint startIndex; |
+ |
+#ifdef NETTY_TCNATIVE_BIO_DEBUG |
+ fprintf(stderr, "tcn_write_to_bytebuffer nonApplicationBufferFreeSpace %d\n", nonApplicationBufferFreeSpace); |
+#endif |
+ if (nonApplicationBufferFreeSpace == 0) { |
+ BIO_set_retry_write(bio); /* buffer is full */ |
+ return -1; |
+ } |
+ |
+ writeAmount = TCN_MIN(nonApplicationBufferFreeSpace, (jint) inl) * sizeof(char); |
+ startIndex = bioUserData->nonApplicationBufferOffset + bioUserData->nonApplicationBufferLength; |
+ writeChunk = bioUserData->nonApplicationBufferSize - startIndex; |
+ |
+#ifdef NETTY_TCNATIVE_BIO_DEBUG |
+ fprintf(stderr, "tcn_write_to_bytebuffer bioUserData->nonApplicationBufferLength %d bioUserData->nonApplicationBufferOffset %d startIndex %d writeChunk %d writeAmount %d\n", bioUserData->nonApplicationBufferLength, bioUserData->nonApplicationBufferOffset, startIndex, writeChunk, writeAmount); |
+#endif |
+ |
+ // check if the write will wrap around the buffer. |
+ if (writeAmount > writeChunk) { |
+ memcpy(&bioUserData->nonApplicationBuffer[startIndex], in, (size_t) writeChunk); |
+ memcpy(bioUserData->nonApplicationBuffer, &in[writeChunk], (size_t) (writeAmount - writeChunk)); |
+ } else { |
+ memcpy(&bioUserData->nonApplicationBuffer[startIndex], in, (size_t) writeAmount); |
+ } |
+ bioUserData->nonApplicationBufferLength += writeAmount; |
+ // This write amount will not be used by Java, and doesn't correlate to the ByteBuffer source. |
+ // The internal SSL buffer exists because a SSL_read operation may actually write data (e.g. handshake). |
+ return writeAmount; |
+ } |
+ |
+ if (bioUserData->buffer == NULL || bioUserData->bufferLength == 0) { |
+ BIO_set_retry_write(bio); /* no buffer to write into */ |
+ return -1; |
+ } |
+ |
+ // First check if we need to drain data queued in the internal SSL buffer. |
+ if (bioUserData->nonApplicationBufferLength != 0) { |
+ writeAmount = tcn_flush_sslbuffer_to_bytebuffer(bioUserData); |
+ } |
+ |
+ // Next write "in" into what ever space the ByteBuffer has available. |
+ writeChunk = TCN_MIN(bioUserData->bufferLength, (jint) inl) * sizeof(char); |
+ |
+#ifdef NETTY_TCNATIVE_BIO_DEBUG |
+ fprintf(stderr, "tcn_write_to_bytebuffer2 writeChunk %d\n", writeChunk); |
+#endif |
+ |
+ memcpy(bioUserData->buffer, in, (size_t) writeChunk); |
+ bioUserData->bufferLength -= writeChunk; |
+ bioUserData->buffer += writeChunk; // Pointer arithmetic based on char* type |
+ |
+ return writeAmount + writeChunk; |
+} |
+ |
+static jint tcn_read_from_bytebuffer(BIO* bio, char *out, int outl) { |
+ jint readAmount; |
+ struct TCN_bio_bytebuffer* bioUserData = (struct TCN_bio_bytebuffer*) BIO_get_data(bio); |
+ TCN_ASSERT(bioUserData != NULL); |
+ |
+#ifdef NETTY_TCNATIVE_BIO_DEBUG |
+ fprintf(stderr, "tcn_read_from_bytebuffer bioUserData->bufferIsSSLWriteSink %d outl %d [%.*s]\n", bioUserData->bufferIsSSLWriteSink, outl, outl, out); |
+#endif |
+ |
+ if (out == NULL || outl <= 0) { |
return 0; |
+ } |
+ |
+ if (bioUserData->bufferIsSSLWriteSink || bioUserData->buffer == NULL || bioUserData->bufferLength == 0) { |
+ // During handshake this may happen, and it means we are not setup to read yet. |
+ BIO_set_retry_read(bio); |
+ return -1; |
+ } |
+ |
+ readAmount = TCN_MIN(bioUserData->bufferLength, (jint) outl) * sizeof(char); |
+ |
+#ifdef NETTY_TCNATIVE_BIO_DEBUG |
+ fprintf(stderr, "tcn_read_from_bytebuffer readAmount %d\n", readAmount); |
#endif |
- BIGNUM *e = BN_new(); |
- RSA *rsa = RSA_new(); |
- int ret = 1; |
+ memcpy(out, bioUserData->buffer, (size_t) readAmount); |
+ bioUserData->bufferLength -= readAmount; |
+ bioUserData->buffer += readAmount; // Pointer arithmetic based on char* type |
- if (e == NULL || |
- rsa == NULL || |
- !BN_set_word(e, RSA_F4) || |
- RSA_generate_key_ex(rsa, bits, e, NULL) != 1) { |
- goto err; |
+ return readAmount; |
+} |
+ |
+static int bio_java_bytebuffer_create(BIO* bio) { |
+ struct TCN_bio_bytebuffer* bioUserData = (struct TCN_bio_bytebuffer*) OPENSSL_malloc(sizeof(struct TCN_bio_bytebuffer)); |
+ if (bioUserData == NULL) { |
+ return 0; |
} |
+ // The actual ByteBuffer is set from java and may be swapped out for each operation. |
+ bioUserData->buffer = NULL; |
+ bioUserData->bufferLength = 0; |
+ bioUserData->bufferIsSSLWriteSink = false; |
+ bioUserData->nonApplicationBuffer = NULL; |
+ bioUserData->nonApplicationBufferSize = 0; |
+ bioUserData->nonApplicationBufferOffset = 0; |
+ bioUserData->nonApplicationBufferLength = 0; |
- SSL_temp_keys[idx] = rsa; |
- rsa = NULL; |
- ret = 0; |
+ BIO_set_data(bio, bioUserData); |
-err: |
- BN_free(e); |
- RSA_free(rsa); |
- return ret; |
-#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ |
+ // In order to for OpenSSL to properly manage the lifetime of a BIO it relies on some shutdown and init state. |
+ // The behavior expected by OpenSSL can be found here: https://www.openssl.org/docs/man1.1.0/crypto/BIO_set_data.html |
+ BIO_set_shutdown(bio, 1); |
+ BIO_set_init(bio, 1); |
+ |
+ return 1; |
} |
-static int ssl_tmp_key_init_dh(int bits, int idx) |
-{ |
-#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(OPENSSL_USE_DEPRECATED) |
- if (!(SSL_temp_keys[idx] = |
- SSL_dh_get_tmp_param(bits))) |
+static int bio_java_bytebuffer_destroy(BIO* bio) { |
+ struct TCN_bio_bytebuffer* bioUserData; |
+ |
+ if (bio == NULL) { |
+ return 0; |
+ } |
+ |
+ bioUserData = (struct TCN_bio_bytebuffer*) BIO_get_data(bio); |
+ if (bioUserData == NULL) { |
return 1; |
- else |
+ } |
+ |
+ if (bioUserData->nonApplicationBuffer != NULL) { |
+ OPENSSL_free(bioUserData->nonApplicationBuffer); |
+ bioUserData->nonApplicationBuffer = NULL; |
+ } |
+ |
+ // The buffer is not owned by tcn, so just free the native memory. |
+ OPENSSL_free(bioUserData); |
+ BIO_set_data(bio, NULL); |
+ |
+ return 1; |
+} |
+ |
+static int bio_java_bytebuffer_write(BIO* bio, const char* in, int inl) { |
+ BIO_clear_retry_flags(bio); |
+ return (int) tcn_write_to_bytebuffer(bio, in, inl); |
+} |
+ |
+static int bio_java_bytebuffer_read(BIO* bio, char* out, int outl) { |
+ BIO_clear_retry_flags(bio); |
+ return (int) tcn_read_from_bytebuffer(bio, out, outl); |
+} |
+ |
+static int bio_java_bytebuffer_puts(BIO* bio, const char *in) { |
+ BIO_clear_retry_flags(bio); |
+ return (int) tcn_write_to_bytebuffer(bio, in, strlen(in)); |
+} |
+ |
+static int bio_java_bytebuffer_gets(BIO* b, char* out, int outl) { |
+ // Not supported https://www.openssl.org/docs/man1.0.2/crypto/BIO_write.html |
+ return -2; |
+} |
+ |
+static long bio_java_bytebuffer_ctrl(BIO* bio, int cmd, long num, void* ptr) { |
+ // see https://www.openssl.org/docs/man1.0.1/crypto/BIO_ctrl.html |
+ switch (cmd) { |
+ case BIO_CTRL_GET_CLOSE: |
+ return (long) BIO_get_shutdown(bio); |
+ case BIO_CTRL_SET_CLOSE: |
+ BIO_set_shutdown(bio, (int) num); |
+ return 1; |
+ case BIO_CTRL_FLUSH: |
+ return 1; |
+ default: |
+ return 0; |
+ } |
+} |
+ |
+TCN_IMPLEMENT_CALL(jint, SSL, bioLengthByteBuffer)(TCN_STDARGS, jlong bioAddress) { |
+ BIO* bio = J2P(bioAddress, BIO*); |
+ struct TCN_bio_bytebuffer* bioUserData; |
+ |
+ if (bio == NULL) { |
+ tcn_ThrowException(e, "bio is null"); |
+ return 0; |
+ } |
+ |
+ bioUserData = (struct TCN_bio_bytebuffer*) BIO_get_data(bio); |
+ return bioUserData == NULL ? 0 : bioUserData->bufferLength; |
+} |
+ |
+TCN_IMPLEMENT_CALL(jint, SSL, bioLengthNonApplication)(TCN_STDARGS, jlong bioAddress) { |
+ BIO* bio = J2P(bioAddress, BIO*); |
+ struct TCN_bio_bytebuffer* bioUserData; |
+ |
+ if (bio == NULL) { |
+ tcn_ThrowException(e, "bio is null"); |
return 0; |
+ } |
+ |
+ bioUserData = (struct TCN_bio_bytebuffer*) BIO_get_data(bio); |
+ return bioUserData == NULL ? 0 : bioUserData->nonApplicationBufferLength; |
+} |
+ |
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) |
+static BIO_METHOD bio_java_bytebuffer_methods = { |
+ BIO_TYPE_MEM, |
+ "Java ByteBuffer", |
+ bio_java_bytebuffer_write, |
+ bio_java_bytebuffer_read, |
+ bio_java_bytebuffer_puts, |
+ bio_java_bytebuffer_gets, |
+ bio_java_bytebuffer_ctrl, |
+ bio_java_bytebuffer_create, |
+ bio_java_bytebuffer_destroy, |
+ NULL |
+}; |
#else |
- return 0; |
+static BIO_METHOD* bio_java_bytebuffer_methods = NULL; |
+ |
+static void init_bio_methods(void) { |
+ bio_java_bytebuffer_methods = BIO_meth_new(BIO_TYPE_MEM, "Java ByteBuffer"); |
+ BIO_meth_set_write(bio_java_bytebuffer_methods, &bio_java_bytebuffer_write); |
+ BIO_meth_set_read(bio_java_bytebuffer_methods, &bio_java_bytebuffer_read); |
+ BIO_meth_set_puts(bio_java_bytebuffer_methods, &bio_java_bytebuffer_puts); |
+ BIO_meth_set_gets(bio_java_bytebuffer_methods, &bio_java_bytebuffer_gets); |
+ BIO_meth_set_ctrl(bio_java_bytebuffer_methods, &bio_java_bytebuffer_ctrl); |
+ BIO_meth_set_create(bio_java_bytebuffer_methods, &bio_java_bytebuffer_create); |
+ BIO_meth_set_destroy(bio_java_bytebuffer_methods, &bio_java_bytebuffer_destroy); |
+} |
+ |
+static void free_bio_methods(void) { |
+ BIO_meth_free(bio_java_bytebuffer_methods); |
+} |
+#endif |
+ |
+static BIO_METHOD* BIO_java_bytebuffer() { |
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) |
+ return &bio_java_bytebuffer_methods; |
+#else |
+ return bio_java_bytebuffer_methods; |
#endif |
} |
+static int ssl_tmp_key_init_dh(int bits, int idx) |
+{ |
+#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(OPENSSL_USE_DEPRECATED) || defined(LIBRESSL_VERSION_NUMBER) |
+ return (SSL_temp_keys[idx] = SSL_dh_get_tmp_param(bits)) ? 0 : 1; |
+#else |
+ return 0; |
+#endif |
+} |
TCN_IMPLEMENT_CALL(jint, SSL, version)(TCN_STDARGS) |
{ |
UNREFERENCED_STDARGS; |
- return (jint) SSLeay(); |
+ return OpenSSL_version_num(); |
} |
TCN_IMPLEMENT_CALL(jstring, SSL, versionString)(TCN_STDARGS) |
{ |
UNREFERENCED(o); |
- return AJP_TO_JSTRING(SSLeay_version(SSLEAY_VERSION)); |
+ return AJP_TO_JSTRING(OpenSSL_version(OPENSSL_VERSION)); |
} |
/* |
@@ -302,14 +552,6 @@ static apr_status_t ssl_init_cleanup(void *data) |
return APR_SUCCESS; |
ssl_initialized = 0; |
- if (tcn_password_callback.cb.obj) { |
- JNIEnv *env; |
- tcn_get_java_env(&env); |
- TCN_UNLOAD_CLASS(env, |
- tcn_password_callback.cb.obj); |
- } |
- |
- SSL_TMP_KEYS_FREE(RSA); |
SSL_TMP_KEYS_FREE(DH); |
/* |
* Try to kill the internals of the SSL library. |
@@ -329,7 +571,12 @@ static apr_status_t ssl_init_cleanup(void *data) |
#if OPENSSL_VERSION_NUMBER >= 0x00907001 |
CRYPTO_cleanup_all_ex_data(); |
#endif |
- ERR_remove_state(0); |
+#if OPENSSL_VERSION_NUMBER < 0x10100000L |
+ ERR_remove_thread_state(NULL); |
+#endif |
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) |
+ free_bio_methods(); |
+#endif |
/* Don't call ERR_free_strings here; ERR_load_*_strings only |
* actually load the error strings once per process due to static |
@@ -401,11 +648,16 @@ static unsigned long ssl_thread_id(void) |
#endif |
} |
+static void ssl_set_thread_id(CRYPTO_THREADID *id) |
+{ |
+ CRYPTO_THREADID_set_numeric(id, ssl_thread_id()); |
+} |
+ |
static apr_status_t ssl_thread_cleanup(void *data) |
{ |
UNREFERENCED(data); |
CRYPTO_set_locking_callback(NULL); |
- CRYPTO_set_id_callback(NULL); |
+ CRYPTO_THREADID_set_callback(NULL); |
CRYPTO_set_dynlock_create_callback(NULL); |
CRYPTO_set_dynlock_lock_callback(NULL); |
CRYPTO_set_dynlock_destroy_callback(NULL); |
@@ -466,8 +718,6 @@ static struct CRYPTO_dynlock_value *ssl_dyn_create_function(const char *file, |
static void ssl_dyn_lock_function(int mode, struct CRYPTO_dynlock_value *l, |
const char *file, int line) |
{ |
- |
- |
if (mode & CRYPTO_LOCK) { |
apr_thread_mutex_lock(l->mutex); |
} |
@@ -493,6 +743,7 @@ static void ssl_dyn_destroy_function(struct CRYPTO_dynlock_value *l, |
*/ |
apr_pool_destroy(l->pool); |
} |
+ |
static void ssl_thread_setup(apr_pool_t *p) |
{ |
int i; |
@@ -505,7 +756,7 @@ static void ssl_thread_setup(apr_pool_t *p) |
APR_THREAD_MUTEX_DEFAULT, p); |
} |
- CRYPTO_set_id_callback(ssl_thread_id); |
+ CRYPTO_THREADID_set_callback(ssl_set_thread_id); |
CRYPTO_set_locking_callback(ssl_thread_lock); |
/* Set up dynamic locking scaffolding for OpenSSL to use at its |
@@ -520,113 +771,9 @@ static void ssl_thread_setup(apr_pool_t *p) |
apr_pool_cleanup_null); |
} |
-static int ssl_rand_choosenum(int l, int h) |
-{ |
- int i; |
- char buf[50]; |
- |
- apr_snprintf(buf, sizeof(buf), "%.0f", |
- (((double)(rand()%RAND_MAX)/RAND_MAX)*(h-l))); |
- i = atoi(buf)+1; |
- if (i < l) i = l; |
- if (i > h) i = h; |
- return i; |
-} |
- |
-static int ssl_rand_load_file(const char *file) |
-{ |
- int n; |
- |
- if (file == NULL) |
- file = ssl_global_rand_file; |
- if (file && (strcmp(file, "builtin") == 0)) |
- return -1; |
-// BoringSsl doesn't support RAND_file_name, but RAND_status() returns 1 anyways |
-#ifndef OPENSSL_IS_BORINGSSL |
- if (file == NULL) { |
- char buffer[APR_PATH_MAX]; |
- file = RAND_file_name(buffer, sizeof(buffer)); |
- } |
-#endif |
- if (file) { |
-#ifdef HAVE_SSL_RAND_EGD |
- if (strncmp(file, "egd:", 4) == 0) { |
- if ((n = RAND_egd(file + 4)) > 0) |
- return n; |
- else |
- return -1; |
- } |
-#endif |
- if ((n = RAND_load_file(file, -1)) > 0) |
- return n; |
- } |
- return -1; |
-} |
- |
-/* |
- * writes a number of random bytes (currently 1024) to |
- * file which can be used to initialize the PRNG by calling |
- * RAND_load_file() in a later session |
- */ |
-static int ssl_rand_save_file(const char *file) |
-{ |
-#ifndef OPENSSL_IS_BORINGSSL |
- char buffer[APR_PATH_MAX]; |
- int n; |
- if (file == NULL) { |
- file = RAND_file_name(buffer, sizeof(buffer)); |
-#ifdef HAVE_SSL_RAND_EGD |
- } else if ((n = RAND_egd(file)) > 0) { |
- return 0; |
-#endif |
- } |
- if (file == NULL || !RAND_write_file(file)) |
- return 0; |
- else |
- return 1; |
-#else |
- // BoringSsl doesn't have RAND_file_name/RAND_write_file and RAND_egd always return 255 |
- return 0; |
-#endif |
-} |
- |
-int SSL_rand_seed(const char *file) |
-{ |
- unsigned char stackdata[256]; |
- static volatile apr_uint32_t counter = 0; |
- |
- if (ssl_rand_load_file(file) < 0) { |
- int n; |
- struct { |
- apr_time_t t; |
- pid_t p; |
- unsigned long i; |
- apr_uint32_t u; |
- } _ssl_seed; |
- if (counter == 0) { |
- apr_generate_random_bytes(stackdata, 256); |
- RAND_seed(stackdata, 128); |
- } |
- _ssl_seed.t = apr_time_now(); |
- _ssl_seed.p = getpid(); |
- _ssl_seed.i = ssl_thread_id(); |
- apr_atomic_inc32(&counter); |
- _ssl_seed.u = counter; |
- RAND_seed((unsigned char *)&_ssl_seed, sizeof(_ssl_seed)); |
- /* |
- * seed in some current state of the run-time stack (128 bytes) |
- */ |
- n = ssl_rand_choosenum(0, sizeof(stackdata)-128-1); |
- RAND_seed(stackdata + n, 128); |
- } |
- return RAND_status(); |
-} |
- |
TCN_IMPLEMENT_CALL(jint, SSL, initialize)(TCN_STDARGS, jstring engine) |
{ |
int r = 0; |
- jclass clazz; |
- jclass sClazz; |
TCN_ALLOC_CSTRING(engine); |
@@ -642,18 +789,20 @@ TCN_IMPLEMENT_CALL(jint, SSL, initialize)(TCN_STDARGS, jstring engine) |
return (jint)APR_SUCCESS; |
} |
+#if OPENSSL_VERSION_NUMBER < 0x10100000L |
if (SSLeay() < 0x0090700L) { |
TCN_FREE_CSTRING(engine); |
tcn_ThrowAPRException(e, APR_EINVAL); |
ssl_initialized = 0; |
return (jint)APR_EINVAL; |
} |
+#endif |
-#ifndef OPENSSL_IS_BORINGSSL |
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) |
/* We must register the library in full, to ensure our configuration |
* code can successfully test the SSL environment. |
*/ |
- CRYPTO_malloc_init(); |
+ OPENSSL_malloc_init(); |
#endif |
ERR_load_crypto_strings(); |
@@ -663,7 +812,7 @@ TCN_IMPLEMENT_CALL(jint, SSL, initialize)(TCN_STDARGS, jstring engine) |
#if HAVE_ENGINE_LOAD_BUILTIN_ENGINES |
ENGINE_load_builtin_engines(); |
#endif |
-#if OPENSSL_VERSION_NUMBER >= 0x00907001 && !defined(OPENSSL_IS_BORINGSSL) |
+#if OPENSSL_VERSION_NUMBER >= 0x00907001 |
OPENSSL_load_builtin_modules(); |
#endif |
@@ -703,17 +852,16 @@ TCN_IMPLEMENT_CALL(jint, SSL, initialize)(TCN_STDARGS, jstring engine) |
} |
#endif |
- memset(&tcn_password_callback, 0, sizeof(tcn_pass_cb_t)); |
- /* Initialize PRNG |
- * This will in most cases call the builtin |
- * low entropy seed. |
- */ |
- SSL_rand_seed(NULL); |
- /* For SSL_get_app_data2() and SSL_get_app_data3() at request time */ |
- SSL_init_app_data2_3_idx(); |
+ // For SSL_get_app_data*() at request time |
+ SSL_init_app_data_idx(); |
+ |
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) |
+ init_bio_methods(); |
+#endif |
SSL_TMP_KEYS_INIT(r); |
if (r) { |
+ ERR_clear_error(); |
TCN_FREE_CSTRING(engine); |
ssl_init_cleanup(NULL); |
tcn_ThrowAPRException(e, APR_ENOTIMPL); |
@@ -727,299 +875,9 @@ TCN_IMPLEMENT_CALL(jint, SSL, initialize)(TCN_STDARGS, jstring engine) |
apr_pool_cleanup_null); |
TCN_FREE_CSTRING(engine); |
- // Cache the byte[].class for performance reasons |
- clazz = (*e)->FindClass(e, "[B"); |
- byteArrayClass = (jclass) (*e)->NewGlobalRef(e, clazz); |
- |
- // Cache the String.class for performance reasons |
- sClazz = (*e)->FindClass(e, "java/lang/String"); |
- stringClass = (jclass) (*e)->NewGlobalRef(e, sClazz); |
- |
return (jint)APR_SUCCESS; |
} |
-TCN_IMPLEMENT_CALL(jboolean, SSL, randLoad)(TCN_STDARGS, jstring file) |
-{ |
- TCN_ALLOC_CSTRING(file); |
- int r; |
- UNREFERENCED(o); |
- r = SSL_rand_seed(J2S(file)); |
- TCN_FREE_CSTRING(file); |
- return r ? JNI_TRUE : JNI_FALSE; |
-} |
- |
-TCN_IMPLEMENT_CALL(jboolean, SSL, randSave)(TCN_STDARGS, jstring file) |
-{ |
- TCN_ALLOC_CSTRING(file); |
- int r; |
- UNREFERENCED(o); |
- r = ssl_rand_save_file(J2S(file)); |
- TCN_FREE_CSTRING(file); |
- return r ? JNI_TRUE : JNI_FALSE; |
-} |
- |
-TCN_IMPLEMENT_CALL(void, SSL, randSet)(TCN_STDARGS, jstring file) |
-{ |
- TCN_ALLOC_CSTRING(file); |
- UNREFERENCED(o); |
- if (J2S(file)) { |
- ssl_global_rand_file = apr_pstrdup(tcn_global_pool, J2S(file)); |
- } |
- TCN_FREE_CSTRING(file); |
-} |
- |
-TCN_IMPLEMENT_CALL(jint, SSL, fipsModeGet)(TCN_STDARGS) |
-{ |
- UNREFERENCED(o); |
-#ifdef OPENSSL_FIPS |
- return FIPS_mode(); |
-#else |
- /* FIPS is unavailable */ |
- tcn_ThrowException(e, "FIPS was not available to tcnative at build time. You will need to re-build tcnative against an OpenSSL with FIPS."); |
- |
- return 0; |
-#endif |
-} |
- |
-TCN_IMPLEMENT_CALL(jint, SSL, fipsModeSet)(TCN_STDARGS, jint mode) |
-{ |
- int r = 0; |
- UNREFERENCED(o); |
- |
-#ifdef OPENSSL_FIPS |
- if(1 != (r = (jint)FIPS_mode_set((int)mode))) { |
- /* arrange to get a human-readable error message */ |
- unsigned long err = ERR_get_error(); |
- char msg[256]; |
- |
- /* ERR_load_crypto_strings() already called in initialize() */ |
- |
- ERR_error_string_n(err, msg, 256); |
- |
- tcn_ThrowException(e, msg); |
- } |
-#else |
- /* FIPS is unavailable */ |
- tcn_ThrowException(e, "FIPS was not available to tcnative at build time. You will need to re-build tcnative against an OpenSSL with FIPS."); |
-#endif |
- |
- return r; |
-} |
- |
-/* OpenSSL Java Stream BIO */ |
- |
-typedef struct { |
- int refcount; |
- apr_pool_t *pool; |
- tcn_callback_t cb; |
-} BIO_JAVA; |
- |
- |
-static apr_status_t generic_bio_cleanup(void *data) |
-{ |
- BIO *b = (BIO *)data; |
- |
- if (b) { |
- BIO_free(b); |
- } |
- return APR_SUCCESS; |
-} |
- |
-void SSL_BIO_close(BIO *bi) |
-{ |
- if (bi == NULL) |
- return; |
- if (bi->ptr != NULL && (bi->flags & SSL_BIO_FLAG_CALLBACK)) { |
- BIO_JAVA *j = (BIO_JAVA *)bi->ptr; |
- j->refcount--; |
- if (j->refcount == 0) { |
- if (j->pool) |
- apr_pool_cleanup_run(j->pool, bi, generic_bio_cleanup); |
- else |
- BIO_free(bi); |
- } |
- } |
- else |
- BIO_free(bi); |
-} |
- |
-void SSL_BIO_doref(BIO *bi) |
-{ |
- if (bi == NULL) |
- return; |
- if (bi->ptr != NULL && (bi->flags & SSL_BIO_FLAG_CALLBACK)) { |
- BIO_JAVA *j = (BIO_JAVA *)bi->ptr; |
- j->refcount++; |
- } |
-} |
- |
- |
-static int jbs_new(BIO *bi) |
-{ |
- BIO_JAVA *j; |
- |
- if ((j = OPENSSL_malloc(sizeof(BIO_JAVA))) == NULL) |
- return 0; |
- j->pool = NULL; |
- j->refcount = 1; |
- bi->shutdown = 1; |
- bi->init = 0; |
- bi->num = -1; |
- bi->ptr = (char *)j; |
- |
- return 1; |
-} |
- |
-static int jbs_free(BIO *bi) |
-{ |
- JNIEnv *e = NULL; |
- BIO_JAVA *j; |
- |
- if (bi == NULL) |
- return 0; |
- if (bi->ptr != NULL) { |
- j = (BIO_JAVA *)bi->ptr; |
- if (bi->init) { |
- bi->init = 0; |
- tcn_get_java_env(&e); |
- TCN_UNLOAD_CLASS(e, j->cb.obj); |
- } |
- OPENSSL_free(bi->ptr); |
- } |
- bi->ptr = NULL; |
- return 1; |
-} |
- |
-static int jbs_write(BIO *b, const char *in, int inl) |
-{ |
- jint ret = -1; |
- |
- if (b->init && in != NULL) { |
- BIO_JAVA *j = (BIO_JAVA *)b->ptr; |
- JNIEnv *e = NULL; |
- jbyteArray jb; |
- tcn_get_java_env(&e); |
- jb = (*e)->NewByteArray(e, inl); |
- if (!(*e)->ExceptionOccurred(e)) { |
- BIO_clear_retry_flags(b); |
- (*e)->SetByteArrayRegion(e, jb, 0, inl, (jbyte *)in); |
- ret = (*e)->CallIntMethod(e, j->cb.obj, |
- j->cb.mid[0], jb); |
- (*e)->DeleteLocalRef(e, jb); |
- } |
- } |
- if (ret == 0) { |
- BIO_set_retry_write(b); |
- ret = -1; |
- } |
- return ret; |
-} |
- |
-static int jbs_read(BIO *b, char *out, int outl) |
-{ |
- jint ret = 0; |
- jbyte *jout; |
- |
- if (b->init && out != NULL) { |
- BIO_JAVA *j = (BIO_JAVA *)b->ptr; |
- JNIEnv *e = NULL; |
- jbyteArray jb; |
- tcn_get_java_env(&e); |
- jb = (*e)->NewByteArray(e, outl); |
- if (!(*e)->ExceptionOccurred(e)) { |
- BIO_clear_retry_flags(b); |
- ret = (*e)->CallIntMethod(e, j->cb.obj, |
- j->cb.mid[1], jb); |
- if (ret > 0) { |
- jout = (*e)->GetPrimitiveArrayCritical(e, jb, NULL); |
- memcpy(out, jout, ret); |
- (*e)->ReleasePrimitiveArrayCritical(e, jb, jout, 0); |
- } else if (outl != 0) { |
- ret = -1; |
- BIO_set_retry_read(b); |
- } |
- (*e)->DeleteLocalRef(e, jb); |
- } |
- } |
- return ret; |
-} |
- |
-static int jbs_puts(BIO *b, const char *in) |
-{ |
- int ret = 0; |
- JNIEnv *e = NULL; |
- BIO_JAVA *j; |
- |
- if (b->init && in != NULL) { |
- j = (BIO_JAVA *)b->ptr; |
- tcn_get_java_env(&e); |
- ret = (*e)->CallIntMethod(e, j->cb.obj, |
- j->cb.mid[2], |
- tcn_new_string(e, in)); |
- } |
- return ret; |
-} |
- |
-static int jbs_gets(BIO *b, char *out, int outl) |
-{ |
- int ret = 0; |
- JNIEnv *e = NULL; |
- BIO_JAVA *j; |
- jobject o; |
- int l; |
- |
- if (b->init && out != NULL) { |
- j = (BIO_JAVA *)b->ptr; |
- tcn_get_java_env(&e); |
- if ((o = (*e)->CallObjectMethod(e, j->cb.obj, |
- j->cb.mid[3], (jint)(outl - 1)))) { |
- TCN_ALLOC_CSTRING(o); |
- if (J2S(o)) { |
- l = (int)strlen(J2S(o)); |
- if (l < outl) { |
- strcpy(out, J2S(o)); |
- ret = outl; |
- } |
- } |
- TCN_FREE_CSTRING(o); |
- } |
- } |
- return ret; |
-} |
- |
-static long jbs_ctrl(BIO *b, int cmd, long num, void *ptr) |
-{ |
- int ret = 0; |
- switch (cmd) { |
- case BIO_CTRL_FLUSH: |
- ret = 1; |
- break; |
- default: |
- ret = 0; |
- break; |
- } |
- return ret; |
-} |
- |
-static BIO_METHOD jbs_methods = { |
- BIO_TYPE_FILE, |
- "Java Callback", |
- jbs_write, |
- jbs_read, |
- jbs_puts, |
- jbs_gets, |
- jbs_ctrl, |
- jbs_new, |
- jbs_free, |
- NULL |
-}; |
- |
-static BIO_METHOD *BIO_jbs() |
-{ |
- return(&jbs_methods); |
-} |
- |
- |
TCN_IMPLEMENT_CALL(jlong, SSL, newMemBIO)(TCN_STDARGS) |
{ |
BIO *bio = NULL; |
@@ -1029,138 +887,14 @@ TCN_IMPLEMENT_CALL(jlong, SSL, newMemBIO)(TCN_STDARGS) |
// TODO: Use BIO_s_secmem() once included in stable release |
if ((bio = BIO_new(BIO_s_mem())) == NULL) { |
tcn_ThrowException(e, "Create BIO failed"); |
- return (jlong) NULL; |
- } |
- return P2J(bio); |
-} |
- |
-TCN_IMPLEMENT_CALL(jlong, SSL, newBIO)(TCN_STDARGS, jlong pool, |
- jobject callback) |
-{ |
- BIO *bio = NULL; |
- BIO_JAVA *j; |
- jclass cls; |
- |
- UNREFERENCED(o); |
- |
- if ((bio = BIO_new(BIO_jbs())) == NULL) { |
- tcn_ThrowException(e, "Create BIO failed"); |
- goto init_failed; |
- } |
- j = (BIO_JAVA *)bio->ptr; |
- if (j == NULL) { |
- tcn_ThrowException(e, "Create BIO failed"); |
- goto init_failed; |
- } |
- j->pool = J2P(pool, apr_pool_t *); |
- if (j->pool) { |
- apr_pool_cleanup_register(j->pool, (const void *)bio, |
- generic_bio_cleanup, |
- apr_pool_cleanup_null); |
+ return 0; |
} |
- |
- cls = (*e)->GetObjectClass(e, callback); |
- j->cb.mid[0] = (*e)->GetMethodID(e, cls, "write", "([B)I"); |
- j->cb.mid[1] = (*e)->GetMethodID(e, cls, "read", "([B)I"); |
- j->cb.mid[2] = (*e)->GetMethodID(e, cls, "puts", "(Ljava/lang/String;)I"); |
- j->cb.mid[3] = (*e)->GetMethodID(e, cls, "gets", "(I)Ljava/lang/String;"); |
- /* TODO: Check if method id's are valid */ |
- j->cb.obj = (*e)->NewGlobalRef(e, callback); |
- |
- bio->init = 1; |
- bio->flags = SSL_BIO_FLAG_CALLBACK; |
return P2J(bio); |
-init_failed: |
- return 0; |
-} |
- |
- |
-TCN_IMPLEMENT_CALL(jint, SSL, closeBIO)(TCN_STDARGS, jlong bio) |
-{ |
- BIO *b = J2P(bio, BIO *); |
- |
- UNREFERENCED_STDARGS; |
- |
- if (b != NULL) { |
- SSL_BIO_close(b); |
- } |
- |
- return APR_SUCCESS; |
-} |
- |
-TCN_IMPLEMENT_CALL(void, SSL, setPasswordCallback)(TCN_STDARGS, |
- jobject callback) |
-{ |
- jclass cls; |
- |
- UNREFERENCED(o); |
- if (tcn_password_callback.cb.obj) { |
- TCN_UNLOAD_CLASS(e, |
- tcn_password_callback.cb.obj); |
- } |
- cls = (*e)->GetObjectClass(e, callback); |
- tcn_password_callback.cb.mid[0] = (*e)->GetMethodID(e, cls, "callback", |
- "(Ljava/lang/String;)Ljava/lang/String;"); |
- /* TODO: Check if method id is valid */ |
- tcn_password_callback.cb.obj = (*e)->NewGlobalRef(e, callback); |
- |
-} |
- |
-TCN_IMPLEMENT_CALL(void, SSL, setPassword)(TCN_STDARGS, jstring password) |
-{ |
- TCN_ALLOC_CSTRING(password); |
- UNREFERENCED(o); |
- if (J2S(password)) { |
- strncpy(tcn_password_callback.password, J2S(password), SSL_MAX_PASSWORD_LEN); |
- tcn_password_callback.password[SSL_MAX_PASSWORD_LEN-1] = '\0'; |
- } |
- TCN_FREE_CSTRING(password); |
-} |
- |
-TCN_IMPLEMENT_CALL(jboolean, SSL, generateRSATempKey)(TCN_STDARGS, jint idx) |
-{ |
- int r = 1; |
- UNREFERENCED_STDARGS; |
- SSL_TMP_KEY_FREE(RSA, idx); |
- switch (idx) { |
- case SSL_TMP_KEY_RSA_512: |
- r = SSL_TMP_KEY_INIT_RSA(512); |
- break; |
- case SSL_TMP_KEY_RSA_1024: |
- r = SSL_TMP_KEY_INIT_RSA(1024); |
- break; |
- case SSL_TMP_KEY_RSA_2048: |
- r = SSL_TMP_KEY_INIT_RSA(2048); |
- break; |
- case SSL_TMP_KEY_RSA_4096: |
- r = SSL_TMP_KEY_INIT_RSA(4096); |
- break; |
- } |
- return r ? JNI_FALSE : JNI_TRUE; |
-} |
- |
-TCN_IMPLEMENT_CALL(jboolean, SSL, loadDSATempKey)(TCN_STDARGS, jint idx, |
- jstring file) |
-{ |
- jboolean r = JNI_FALSE; |
- TCN_ALLOC_CSTRING(file); |
- DH *dh; |
- UNREFERENCED(o); |
- |
- if (!J2S(file)) |
- return JNI_FALSE; |
- SSL_TMP_KEY_FREE(DSA, idx); |
- if ((dh = SSL_dh_get_param_from_file(J2S(file)))) { |
- SSL_temp_keys[idx] = dh; |
- r = JNI_TRUE; |
- } |
- TCN_FREE_CSTRING(file); |
- return r; |
} |
TCN_IMPLEMENT_CALL(jstring, SSL, getLastError)(TCN_STDARGS) |
{ |
- char buf[256]; |
+ char buf[ERR_LEN]; |
UNREFERENCED(o); |
ERR_error_string(ERR_get_error(), buf); |
return tcn_new_string(e, buf); |
@@ -1205,14 +939,28 @@ TCN_IMPLEMENT_CALL(jlong /* SSL * */, SSL, newSSL)(TCN_STDARGS, |
UNREFERENCED_STDARGS; |
- handshakeCount = malloc(sizeof(int)); |
ssl = SSL_new(c->ctx); |
if (ssl == NULL) { |
tcn_ThrowException(e, "cannot create new ssl"); |
return 0; |
} |
+ // Set the app_data2 before all the others because it may be used in SSL_free. |
+ SSL_set_app_data2(ssl, c); |
+ |
+ // Initially we will share the configuration from the SSLContext. |
+ // Set this before other app_data because there is no chance of failure, and if other app_data initialization fails |
+ // SSL_free maybe called and the state of this variable is assumed to be initalized. |
+ SSL_set_app_data4(ssl, &c->verify_config); |
+ |
// Store the handshakeCount in the SSL instance. |
+ handshakeCount = (int*) OPENSSL_malloc(sizeof(int)); |
+ if (handshakeCount == NULL) { |
+ SSL_free(ssl); |
+ tcn_ThrowException(e, "cannot create handshakeCount user data"); |
+ return 0; |
+ } |
+ |
*handshakeCount = 0; |
SSL_set_app_data3(ssl, handshakeCount); |
@@ -1225,33 +973,9 @@ TCN_IMPLEMENT_CALL(jlong /* SSL * */, SSL, newSSL)(TCN_STDARGS, |
SSL_set_connect_state(ssl); |
} |
- // Setup verify and seed |
- SSL_set_verify_result(ssl, X509_V_OK); |
- SSL_rand_seed(c->rand_file); |
- |
- // Store for later usage in SSL_callback_SSL_verify |
- SSL_set_app_data2(ssl, c); |
return P2J(ssl); |
} |
-TCN_IMPLEMENT_CALL(void, SSL, setBIO)(TCN_STDARGS, |
- jlong ssl /* SSL * */, |
- jlong rbio /* BIO * */, |
- jlong wbio /* BIO * */) { |
- SSL *ssl_ = J2P(ssl, SSL *); |
- BIO *r = J2P(rbio, BIO *); |
- BIO *w = J2P(wbio, BIO *); |
- |
- if (ssl_ == NULL) { |
- tcn_ThrowException(e, "ssl is null"); |
- return; |
- } |
- |
- UNREFERENCED_STDARGS; |
- |
- SSL_set_bio(ssl_, r, w); |
-} |
- |
TCN_IMPLEMENT_CALL(jint, SSL, getError)(TCN_STDARGS, |
jlong ssl /* SSL * */, |
jint ret) { |
@@ -1267,78 +991,69 @@ TCN_IMPLEMENT_CALL(jint, SSL, getError)(TCN_STDARGS, |
return SSL_get_error(ssl_, ret); |
} |
-// How much did SSL write into this BIO? |
-TCN_IMPLEMENT_CALL(jint /* nbytes */, SSL, pendingWrittenBytesInBIO)(TCN_STDARGS, |
- jlong bio /* BIO * */) { |
- BIO *b = J2P(bio, BIO *); |
- |
- if (b == NULL) { |
- tcn_ThrowException(e, "bio is null"); |
- return 0; |
- } |
- |
- UNREFERENCED_STDARGS; |
- |
- return BIO_ctrl_pending(b); |
-} |
- |
-// How much is available for reading in the given SSL struct? |
-TCN_IMPLEMENT_CALL(jint, SSL, pendingReadableBytesInSSL)(TCN_STDARGS, jlong ssl /* SSL * */) { |
- SSL *ssl_ = J2P(ssl, SSL *); |
- |
- if (ssl_ == NULL) { |
- tcn_ThrowException(e, "ssl is null"); |
- return 0; |
- } |
- |
- UNREFERENCED_STDARGS; |
- |
- return SSL_pending(ssl_); |
-} |
- |
// Write wlen bytes from wbuf into bio |
-TCN_IMPLEMENT_CALL(jint /* status */, SSL, writeToBIO)(TCN_STDARGS, |
- jlong bio /* BIO * */, |
- jlong wbuf /* char* */, |
- jint wlen /* sizeof(wbuf) */) { |
- BIO *b = J2P(bio, BIO *); |
- void *w = J2P(wbuf, void *); |
- |
- if (b == NULL) { |
+TCN_IMPLEMENT_CALL(jint /* status */, SSL, bioWrite)(TCN_STDARGS, |
+ jlong bioAddress /* BIO* */, |
+ jlong wbufAddress /* char* */, |
+ jint wlen /* sizeof(wbuf) */) { |
+ BIO* bio = J2P(bioAddress, BIO*); |
+ void* wbuf = J2P(wbufAddress, void*); |
+ |
+ if (bio == NULL) { |
tcn_ThrowException(e, "bio is null"); |
return 0; |
} |
- if (w == NULL) { |
+ if (wbuf == NULL) { |
tcn_ThrowException(e, "wbuf is null"); |
return 0; |
} |
UNREFERENCED_STDARGS; |
- return BIO_write(b, w, wlen); |
- |
+ return BIO_write(bio, wbuf, wlen); |
} |
-// Read up to rlen bytes from bio into rbuf |
-TCN_IMPLEMENT_CALL(jint /* status */, SSL, readFromBIO)(TCN_STDARGS, |
- jlong bio /* BIO * */, |
- jlong rbuf /* char * */, |
- jint rlen /* sizeof(rbuf) - 1 */) { |
- BIO *b = J2P(bio, BIO *); |
- void *r = J2P(rbuf, void *); |
+TCN_IMPLEMENT_CALL(void, SSL, bioSetByteBuffer)(TCN_STDARGS, |
+ jlong bioAddress /* BIO* */, |
+ jlong bufferAddress /* Address for direct memory */, |
+ jint maxUsableBytes /* max number of bytes to use */, |
+ jboolean isSSLWriteSink) { |
+ BIO* bio = J2P(bioAddress, BIO*); |
+ char* buffer = J2P(bufferAddress, char*); |
+ struct TCN_bio_bytebuffer* bioUserData = NULL; |
+ TCN_ASSERT(bio != NULL); |
+ TCN_ASSERT(buffer != NULL); |
+ |
+ bioUserData = (struct TCN_bio_bytebuffer*) BIO_get_data(bio); |
+ TCN_ASSERT(bioUserData != NULL); |
+ |
+ bioUserData->buffer = buffer; |
+ bioUserData->bufferLength = maxUsableBytes; |
+ bioUserData->bufferIsSSLWriteSink = (bool) isSSLWriteSink; |
+} |
- if (b == NULL) { |
- tcn_ThrowException(e, "bio is null"); |
- return 0; |
- } |
- if (r == NULL) { |
- tcn_ThrowException(e, "rbuf is null"); |
- return 0; |
+TCN_IMPLEMENT_CALL(void, SSL, bioClearByteBuffer)(TCN_STDARGS, jlong bioAddress) { |
+ BIO* bio = J2P(bioAddress, BIO*); |
+ struct TCN_bio_bytebuffer* bioUserData = NULL; |
+ |
+ if (bio == NULL || (bioUserData = (struct TCN_bio_bytebuffer*) BIO_get_data(bio)) == NULL) { |
+ return; |
} |
- UNREFERENCED_STDARGS; |
+ bioUserData->buffer = NULL; |
+ bioUserData->bufferLength = 0; |
+ bioUserData->bufferIsSSLWriteSink = false; |
+} |
+ |
+TCN_IMPLEMENT_CALL(jint, SSL, bioFlushByteBuffer)(TCN_STDARGS, jlong bioAddress) { |
+ BIO* bio = J2P(bioAddress, BIO*); |
+ struct TCN_bio_bytebuffer* bioUserData; |
- return BIO_read(b, r, rlen); |
+ return (bio == NULL || |
+ (bioUserData = (struct TCN_bio_bytebuffer*) BIO_get_data(bio)) == NULL || |
+ bioUserData->nonApplicationBufferLength == 0 || |
+ bioUserData->buffer == NULL || |
+ !bioUserData->bufferIsSSLWriteSink) ? 0 : tcn_flush_sslbuffer_to_bytebuffer(bioUserData); |
} |
// Write up to wlen bytes of application data to the ssl BIO (encrypt) |
@@ -1420,43 +1135,74 @@ TCN_IMPLEMENT_CALL(void, SSL, setShutdown)(TCN_STDARGS, |
TCN_IMPLEMENT_CALL(void, SSL, freeSSL)(TCN_STDARGS, |
jlong ssl /* SSL * */) { |
int *handshakeCount = NULL; |
+ tcn_ssl_ctxt_t* c = NULL; |
+ tcn_ssl_verify_config_t* verify_config = NULL; |
SSL *ssl_ = J2P(ssl, SSL *); |
if (ssl_ == NULL) { |
tcn_ThrowException(e, "ssl is null"); |
return; |
} |
+ c = SSL_get_app_data2(ssl_); |
handshakeCount = SSL_get_app_data3(ssl_); |
+ verify_config = SSL_get_app_data4(ssl_); |
UNREFERENCED_STDARGS; |
+ TCN_ASSERT(c != NULL); |
if (handshakeCount != NULL) { |
- free(handshakeCount); |
+ OPENSSL_free(handshakeCount); |
+ SSL_set_app_data3(ssl_, NULL); |
+ } |
+ |
+ // Only free the verify_config if it is not shared with the SSLContext. |
+ if (verify_config != NULL && verify_config != &c->verify_config) { |
+ OPENSSL_free(verify_config); |
+ SSL_set_app_data4(ssl_, &c->verify_config); |
} |
SSL_free(ssl_); |
} |
-// Make a BIO pair (network and internal) for the provided SSL * and return the network BIO |
-TCN_IMPLEMENT_CALL(jlong, SSL, makeNetworkBIO)(TCN_STDARGS, |
- jlong ssl /* SSL * */) { |
- SSL *ssl_ = J2P(ssl, SSL *); |
- BIO *internal_bio; |
- BIO *network_bio; |
+TCN_IMPLEMENT_CALL(jlong, SSL, bioNewByteBuffer)(TCN_STDARGS, |
+ jlong ssl /* SSL* */, |
+ jint nonApplicationBufferSize) { |
+ SSL* ssl_ = J2P(ssl, SSL*); |
+ BIO* bio; |
+ struct TCN_bio_bytebuffer* bioUserData; |
if (ssl_ == NULL) { |
tcn_ThrowException(e, "ssl is null"); |
return 0; |
} |
- if (BIO_new_bio_pair(&internal_bio, 0, &network_bio, 0) != 1) { |
- tcn_ThrowException(e, "BIO_new_bio_pair failed"); |
+ if (nonApplicationBufferSize <= 0) { |
+ tcn_ThrowException(e, "nonApplicationBufferSize <= 0"); |
return 0; |
} |
- UNREFERENCED(o); |
+ bio = BIO_new(BIO_java_bytebuffer()); |
+ if (bio == NULL) { |
+ tcn_ThrowException(e, "BIO_new failed"); |
+ return 0; |
+ } |
+ |
+ bioUserData = BIO_get_data(bio); |
+ if (bioUserData == NULL) { |
+ BIO_free(bio); |
+ tcn_ThrowException(e, "BIO_get_data failed"); |
+ return 0; |
+ } |
+ |
+ bioUserData->nonApplicationBuffer = (char*) OPENSSL_malloc(nonApplicationBufferSize * sizeof(char)); |
+ if (bioUserData->nonApplicationBuffer == NULL) { |
+ BIO_free(bio); |
+ tcn_Throw(e, "Failed to allocate internal buffer of size %d", nonApplicationBufferSize); |
+ return 0; |
+ } |
+ bioUserData->nonApplicationBufferSize = nonApplicationBufferSize; |
- SSL_set_bio(ssl_, internal_bio, internal_bio); |
+ SSL_set_bio(ssl_, bio, bio); |
- return P2J(network_bio); |
+ return P2J(bio); |
} |
// Free a BIO * (typically, the network BIO) |
@@ -1614,6 +1360,7 @@ TCN_IMPLEMENT_CALL(jobjectArray, SSL, getPeerCertChain)(TCN_STDARGS, |
unsigned char *buf; |
jobjectArray array; |
jbyteArray bArray; |
+ jclass byteArrayClass = tcn_get_byte_array_class(); |
SSL *ssl_ = J2P(ssl, SSL *); |
@@ -1627,7 +1374,7 @@ TCN_IMPLEMENT_CALL(jobjectArray, SSL, getPeerCertChain)(TCN_STDARGS, |
// Get a stack of all certs in the chain. |
sk = SSL_get_peer_cert_chain(ssl_); |
- len = sk_num((_STACK*) sk); |
+ len = sk_X509_num(sk); |
if (len <= 0) { |
// No peer certificate chain as no auth took place yet, or the auth was not successful. |
return NULL; |
@@ -1636,12 +1383,14 @@ TCN_IMPLEMENT_CALL(jobjectArray, SSL, getPeerCertChain)(TCN_STDARGS, |
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) { |
- OPENSSL_free(buf); |
+ if (buf != NULL) { |
+ OPENSSL_free(buf); |
+ } |
// In case of error just return an empty byte[][] |
return (*e)->NewObjectArray(e, 0, byteArrayClass, NULL); |
} |
@@ -1698,7 +1447,7 @@ TCN_IMPLEMENT_CALL(jbyteArray, SSL, getPeerCertificate)(TCN_STDARGS, |
TCN_IMPLEMENT_CALL(jstring, SSL, getErrorString)(TCN_STDARGS, jlong number) |
{ |
- char buf[256]; |
+ char buf[ERR_LEN]; |
UNREFERENCED(o); |
ERR_error_string(number, buf); |
return tcn_new_string(e, buf); |
@@ -1773,11 +1522,10 @@ TCN_IMPLEMENT_CALL(jlong, SSL, setTimeout)(TCN_STDARGS, jlong ssl, jlong seconds |
} |
-TCN_IMPLEMENT_CALL(void, SSL, setVerify)(TCN_STDARGS, jlong ssl, |
- jint level, jint depth) |
+TCN_IMPLEMENT_CALL(void, SSL, setVerify)(TCN_STDARGS, jlong ssl, jint level, jint depth) |
{ |
- tcn_ssl_ctxt_t *c; |
- int verify; |
+ tcn_ssl_verify_config_t* verify_config; |
+ tcn_ssl_ctxt_t* c; |
SSL *ssl_ = J2P(ssl, SSL *); |
if (ssl_ == NULL) { |
@@ -1786,36 +1534,27 @@ TCN_IMPLEMENT_CALL(void, SSL, setVerify)(TCN_STDARGS, jlong ssl, |
} |
c = SSL_get_app_data2(ssl_); |
- |
- verify = SSL_VERIFY_NONE; |
+ verify_config = SSL_get_app_data4(ssl_); |
UNREFERENCED(o); |
- TCN_ASSERT(c->ctx != 0); |
- c->verify_mode = level; |
+ TCN_ASSERT(c != NULL); |
+ TCN_ASSERT(verify_config != NULL); |
- if (c->verify_mode == SSL_CVERIFY_UNSET) |
- c->verify_mode = SSL_CVERIFY_NONE; |
- if (depth > 0) |
- c->verify_depth = depth; |
- /* |
- * Configure callbacks for SSL context |
- */ |
- if (c->verify_mode == SSL_CVERIFY_REQUIRE) |
- verify |= SSL_VERIFY_PEER_STRICT; |
- if ((c->verify_mode == SSL_CVERIFY_OPTIONAL) || |
- (c->verify_mode == SSL_CVERIFY_OPTIONAL_NO_CA)) |
- verify |= SSL_VERIFY_PEER; |
- if (!c->store) { |
- if (SSL_CTX_set_default_verify_paths(c->ctx)) { |
- c->store = SSL_CTX_get_cert_store(c->ctx); |
- X509_STORE_set_flags(c->store, 0); |
- } |
- else { |
- /* XXX: See if this is fatal */ |
- } |
+ // If we are sharing the configuration from the SSLContext we now need to create a new configuration just for this SSL. |
+ if (verify_config == &c->verify_config) { |
+ verify_config = (tcn_ssl_verify_config_t*) OPENSSL_malloc(sizeof(tcn_ssl_verify_config_t)); |
+ if (verify_config == NULL) { |
+ tcn_ThrowException(e, "failed to allocate tcn_ssl_verify_config_t"); |
+ return; |
+ } |
+ // Copy the verify depth form the context in case depth is <0. |
+ verify_config->verify_depth = c->verify_config.verify_depth; |
+ SSL_set_app_data4(ssl_, verify_config); |
} |
- SSL_set_verify(ssl_, verify, SSL_callback_SSL_verify); |
+ // No need to specify a callback for SSL_set_verify because we override the default certificate verification via SSL_CTX_set_cert_verify_callback. |
+ SSL_set_verify(ssl_, tcn_set_verify_config(verify_config, level, depth), NULL); |
+ SSL_set_verify_depth(ssl_, verify_config->verify_depth); |
} |
TCN_IMPLEMENT_CALL(void, SSL, setOptions)(TCN_STDARGS, jlong ssl, |
@@ -1830,15 +1569,24 @@ TCN_IMPLEMENT_CALL(void, SSL, setOptions)(TCN_STDARGS, jlong ssl, |
UNREFERENCED_STDARGS; |
-#ifndef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION |
- /* Clear the flag if not supported */ |
- if (opt & 0x00040000) { |
- opt &= ~0x00040000; |
- } |
-#endif |
SSL_set_options(ssl_, opt); |
} |
+TCN_IMPLEMENT_CALL(void, SSL, clearOptions)(TCN_STDARGS, jlong ssl, |
+ jint opt) |
+{ |
+ SSL *ssl_ = J2P(ssl, SSL *); |
+ |
+ if (ssl_ == NULL) { |
+ tcn_ThrowException(e, "ssl is null"); |
+ return; |
+ } |
+ |
+ UNREFERENCED_STDARGS; |
+ |
+ SSL_clear_options(ssl_, opt); |
+} |
+ |
TCN_IMPLEMENT_CALL(jint, SSL, getOptions)(TCN_STDARGS, jlong ssl) |
{ |
SSL *ssl_ = J2P(ssl, SSL *); |
@@ -1858,7 +1606,7 @@ TCN_IMPLEMENT_CALL(jobjectArray, SSL, getCiphers)(TCN_STDARGS, jlong ssl) |
STACK_OF(SSL_CIPHER) *sk; |
int len; |
jobjectArray array; |
- SSL_CIPHER *cipher; |
+ const SSL_CIPHER *cipher; |
const char *name; |
int i; |
jstring c_name; |
@@ -1872,7 +1620,7 @@ TCN_IMPLEMENT_CALL(jobjectArray, SSL, getCiphers)(TCN_STDARGS, jlong ssl) |
UNREFERENCED_STDARGS; |
sk = SSL_get_ciphers(ssl_); |
- len = sk_num((_STACK*) sk); |
+ len = sk_SSL_CIPHER_num(sk); |
if (len <= 0) { |
// No peer certificate chain as no auth took place yet, or the auth was not successful. |
@@ -1880,10 +1628,10 @@ TCN_IMPLEMENT_CALL(jobjectArray, SSL, getCiphers)(TCN_STDARGS, jlong ssl) |
} |
// Create the byte[][]Â array that holds all the certs |
- array = (*e)->NewObjectArray(e, len, stringClass, NULL); |
+ array = (*e)->NewObjectArray(e, len, tcn_get_string_class(), NULL); |
for (i = 0; i < len; i++) { |
- cipher = (SSL_CIPHER*) sk_value((_STACK*) sk, i); |
+ cipher = sk_SSL_CIPHER_value(sk, i); |
name = SSL_CIPHER_get_name(cipher); |
c_name = (*e)->NewStringUTF(e, name); |
@@ -1911,7 +1659,7 @@ TCN_IMPLEMENT_CALL(jboolean, SSL, setCipherSuites)(TCN_STDARGS, jlong ssl, |
} |
if (!SSL_set_cipher_list(ssl_, J2S(ciphers))) { |
- char err[256]; |
+ char err[ERR_LEN]; |
ERR_error_string(ERR_get_error(), err); |
tcn_Throw(e, "Unable to configure permitted SSL ciphers (%s)", err); |
rv = JNI_FALSE; |
@@ -1965,10 +1713,7 @@ TCN_IMPLEMENT_CALL(jint, SSL, getHandshakeCount)(TCN_STDARGS, jlong ssl) |
UNREFERENCED(o); |
handshakeCount = SSL_get_app_data3(ssl_); |
- if (handshakeCount != NULL) { |
- return *handshakeCount; |
- } |
- return 0; |
+ return handshakeCount != NULL ? *handshakeCount : 0; |
} |
@@ -2007,420 +1752,238 @@ TCN_IMPLEMENT_CALL(void, SSL, setState)(TCN_STDARGS, |
SSL_set_state(ssl_, state); |
} |
+TCN_IMPLEMENT_CALL(void, SSL, setTlsExtHostName)(TCN_STDARGS, jlong ssl, jstring hostname) { |
+ TCN_ALLOC_CSTRING(hostname); |
+ SSL *ssl_ = J2P(ssl, SSL *); |
-/*** End Apple API Additions ***/ |
- |
-#else |
-#error OpenSSL is required! |
- |
-/* OpenSSL is not supported. |
- * Create empty stubs. |
- */ |
- |
-TCN_IMPLEMENT_CALL(jint, SSL, version)(TCN_STDARGS) |
-{ |
- UNREFERENCED_STDARGS; |
- return 0; |
-} |
- |
-TCN_IMPLEMENT_CALL(jstring, SSL, versionString)(TCN_STDARGS) |
-{ |
- UNREFERENCED_STDARGS; |
- return NULL; |
-} |
+ if (ssl_ == NULL) { |
+ tcn_ThrowException(e, "ssl is null"); |
+ } else { |
+ UNREFERENCED(o); |
-TCN_IMPLEMENT_CALL(jint, SSL, initialize)(TCN_STDARGS, jstring engine) |
-{ |
- UNREFERENCED(o); |
- UNREFERENCED(engine); |
- tcn_ThrowAPRException(e, APR_ENOTIMPL); |
- return (jint)APR_ENOTIMPL; |
-} |
+ if (SSL_set_tlsext_host_name(ssl_, J2S(hostname)) != 1) { |
+ char err[ERR_LEN]; |
+ ERR_error_string(ERR_get_error(), err); |
+ tcn_Throw(e, "Unable to set TLS servername extension (%s)", err); |
+ } |
+ } |
-TCN_IMPLEMENT_CALL(jboolean, SSL, randLoad)(TCN_STDARGS, jstring file) |
-{ |
- UNREFERENCED_STDARGS; |
- UNREFERENCED(file); |
- return JNI_FALSE; |
+ TCN_FREE_CSTRING(hostname); |
} |
-TCN_IMPLEMENT_CALL(jboolean, SSL, randSave)(TCN_STDARGS, jstring file) |
-{ |
- UNREFERENCED_STDARGS; |
- return JNI_FALSE; |
-} |
+TCN_IMPLEMENT_CALL(void, SSL, setHostNameValidation)(TCN_STDARGS, jlong sslAddress, jint flags, jstring hostnameString) { |
+ SSL* ssl = J2P(sslAddress, SSL*); |
-TCN_IMPLEMENT_CALL(jboolean, SSL, randMake)(TCN_STDARGS, jstring file, |
- jint length, jboolean base64) |
-{ |
- UNREFERENCED_STDARGS; |
- UNREFERENCED(file); |
- UNREFERENCED(length); |
- UNREFERENCED(base64); |
- return JNI_FALSE; |
+ if (ssl == NULL) { |
+ tcn_ThrowException(e, "ssl is null"); |
+ } else { |
+ const char* hostname = hostnameString == NULL ? NULL : (*e)->GetStringUTFChars(e, hostnameString, 0); |
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(LIBRESSL_VERSION_NUMBER) |
+ X509_VERIFY_PARAM* param = SSL_get0_param(ssl); |
+ X509_VERIFY_PARAM_set_hostflags(param, flags); |
+ if (X509_VERIFY_PARAM_set1_host(param, hostname, 0) != 1) { |
+ char err[ERR_LEN]; |
+ ERR_error_string(ERR_get_error(), err); |
+ tcn_Throw(e, "X509_VERIFY_PARAM_set1_host error (%s)", err); |
+ } |
+#else |
+ if (hostname != NULL && hostname[0] != '\0') { |
+ tcn_ThrowException(e, "hostname verification requires OpenSSL 1.0.2+"); |
+ } |
+#endif |
+ (*e)->ReleaseStringUTFChars(e, hostnameString, hostname); |
+ } |
} |
-TCN_IMPLEMENT_CALL(void, SSL, randSet)(TCN_STDARGS, jstring file) |
-{ |
- UNREFERENCED_STDARGS; |
- UNREFERENCED(file); |
-} |
+TCN_IMPLEMENT_CALL(jobjectArray, SSL, authenticationMethods)(TCN_STDARGS, jlong ssl) { |
+ SSL *ssl_ = J2P(ssl, SSL *); |
+ const STACK_OF(SSL_CIPHER) *ciphers = NULL; |
+ int len; |
+ int i; |
+ jobjectArray array; |
-TCN_IMPLEMENT_CALL(jint, SSL, fipsModeGet)(TCN_STDARGS) |
-{ |
- UNREFERENCED(o); |
- tcn_ThrowException(e, "FIPS was not available to tcnative at build time. You will need to re-build tcnative against an OpenSSL with FIPS."); |
- return 0; |
-} |
+ TCN_ASSERT(ssl_ != NULL); |
-TCN_IMPLEMENT_CALL(jint, SSL, fipsModeSet)(TCN_STDARGS, jint mode) |
-{ |
UNREFERENCED(o); |
- UNREFERENCED(mode); |
- tcn_ThrowException(e, "FIPS was not available to tcnative at build time. You will need to re-build tcnative against an OpenSSL with FIPS."); |
- return 0; |
-} |
- |
-TCN_IMPLEMENT_CALL(jlong, SSL, newBIO)(TCN_STDARGS, jlong pool, |
- jobject callback) |
-{ |
- UNREFERENCED_STDARGS; |
- UNREFERENCED(pool); |
- UNREFERENCED(callback); |
- return 0; |
-} |
- |
-TCN_IMPLEMENT_CALL(jlong, SSL, newMemBIO)(TCN_STDARGS) |
-{ |
- UNREFERENCED_STDARGS; |
- return 0; |
-} |
- |
-TCN_IMPLEMENT_CALL(jint, SSL, closeBIO)(TCN_STDARGS, jlong bio) |
-{ |
- UNREFERENCED_STDARGS; |
- UNREFERENCED(bio); |
- return (jint)APR_ENOTIMPL; |
-} |
- |
-TCN_IMPLEMENT_CALL(void, SSL, setPasswordCallback)(TCN_STDARGS, |
- jobject callback) |
-{ |
- UNREFERENCED_STDARGS; |
- UNREFERENCED(callback); |
-} |
-TCN_IMPLEMENT_CALL(void, SSL, setPassword)(TCN_STDARGS, jstring password) |
-{ |
- UNREFERENCED_STDARGS; |
- UNREFERENCED(password); |
-} |
- |
-TCN_IMPLEMENT_CALL(jboolean, SSL, generateRSATempKey)(TCN_STDARGS, jint idx) |
-{ |
- UNREFERENCED_STDARGS; |
- UNREFERENCED(idx); |
- return JNI_FALSE; |
-} |
+ ciphers = SSL_get_ciphers(ssl_); |
+ len = sk_SSL_CIPHER_num(ciphers); |
-TCN_IMPLEMENT_CALL(jboolean, SSL, loadDSATempKey)(TCN_STDARGS, jint idx, |
- jstring file) |
-{ |
- UNREFERENCED_STDARGS; |
- UNREFERENCED(idx); |
- UNREFERENCED(file); |
- return JNI_FALSE; |
-} |
+ array = (*e)->NewObjectArray(e, len, tcn_get_string_class(), NULL); |
-TCN_IMPLEMENT_CALL(jstring, SSL, getLastError)(TCN_STDARGS) |
-{ |
- UNREFERENCED_STDARGS; |
- return NULL; |
+ for (i = 0; i < len; i++) { |
+ (*e)->SetObjectArrayElement(e, array, i, |
+ (*e)->NewStringUTF(e, SSL_cipher_authentication_method((SSL_CIPHER*) sk_value((_STACK*) ciphers, i)))); |
+ } |
+ return array; |
} |
-TCN_IMPLEMENT_CALL(jboolean, SSL, hasOp)(TCN_STDARGS, jint op) |
+TCN_IMPLEMENT_CALL(void, SSL, setCertificateBio)(TCN_STDARGS, jlong ssl, |
+ jlong cert, jlong key, |
+ jstring password) |
{ |
- UNREFERENCED_STDARGS; |
- UNREFERENCED(op); |
- return JNI_FALSE; |
-} |
- |
-/*** Begin Twitter 1:1 API addition ***/ |
-TCN_IMPLEMENT_CALL(jint, SSL, getLastErrorNumber)(TCN_STDARGS) { |
- UNREFERENCED(o); |
- tcn_ThrowException(e, "Not implemented"); |
- return 0; |
-} |
- |
-TCN_IMPLEMENT_CALL(jlong, SSL, newSSL)(TCN_STDARGS, jlong ssl_ctx) { |
- UNREFERENCED(o); |
- UNREFERENCED(ssl_ctx); |
- tcn_ThrowException(e, "Not implemented"); |
- return 0; |
-} |
- |
-TCN_IMPLEMENT_CALL(void, SSL, setBIO)(TCN_STDARGS, jlong ssl, jlong rbio, jlong wbio) { |
- UNREFERENCED(o); |
- UNREFERENCED(ssl); |
- UNREFERENCED(rbio); |
- UNREFERENCED(wbio); |
- tcn_ThrowException(e, "Not implemented"); |
-} |
- |
-TCN_IMPLEMENT_CALL(jint, SSL, pendingWrittenBytesInBIO)(TCN_STDARGS, jlong bio) { |
- UNREFERENCED(o); |
- UNREFERENCED(bio); |
- tcn_ThrowException(e, "Not implemented"); |
- return 0; |
-} |
- |
-TCN_IMPLEMENT_CALL(jint, SSL, pendingReadableBytesInSSL)(TCN_STDARGS, jlong ssl) { |
- UNREFERENCED(o); |
- UNREFERENCED(ssl); |
- tcn_ThrowException(e, "Not implemented"); |
- return 0; |
-} |
- |
-TCN_IMPLEMENT_CALL(jint, SSL, writeToBIO)(TCN_STDARGS, jlong bio, jlong wbuf, jint wlen) { |
- UNREFERENCED(o); |
- UNREFERENCED(bio); |
- UNREFERENCED(wbuf); |
- UNREFERENCED(wlen); |
- tcn_ThrowException(e, "Not implemented"); |
- return 0; |
-} |
- |
-TCN_IMPLEMENT_CALL(jint, SSL, readFromBIO)(TCN_STDARGS, jlong bio, jlong rbuf, jint rlen) { |
- UNREFERENCED(o); |
- UNREFERENCED(bio); |
- UNREFERENCED(rbuf); |
- UNREFERENCED(rlen); |
- tcn_ThrowException(e, "Not implemented"); |
- return 0; |
-} |
- |
-TCN_IMPLEMENT_CALL(jint, SSL, writeToSSL)(TCN_STDARGS, jlong ssl, jlong wbuf, jint wlen) { |
- UNREFERENCED(o); |
- UNREFERENCED(ssl); |
- UNREFERENCED(wbuf); |
- UNREFERENCED(wlen); |
- tcn_ThrowException(e, "Not implemented"); |
- return 0; |
-} |
- |
-TCN_IMPLEMENT_CALL(jint, SSL, readFromSSL)(TCN_STDARGS, jlong ssl, jlong rbuf, jint rlen) { |
- UNREFERENCED(o); |
- UNREFERENCED(ssl); |
- UNREFERENCED(rbuf); |
- UNREFERENCED(rlen); |
- tcn_ThrowException(e, "Not implemented"); |
- return 0; |
-} |
+ SSL *ssl_ = J2P(ssl, SSL *); |
+ BIO *cert_bio = J2P(cert, BIO *); |
+ BIO *key_bio = J2P(key, BIO *); |
+ EVP_PKEY* pkey = NULL; |
+ X509* xcert = NULL; |
+ TCN_ALLOC_CSTRING(password); |
+ char err[ERR_LEN]; |
-TCN_IMPLEMENT_CALL(jint, SSL, getShutdown)(TCN_STDARGS, jlong ssl) { |
- UNREFERENCED(o); |
- UNREFERENCED(ssl); |
- tcn_ThrowException(e, "Not implemented"); |
- return 0; |
-} |
+ UNREFERENCED(o); |
+ TCN_ASSERT(ssl != NULL); |
-TCN_IMPLEMENT_CALL(void, SSL, setShutdown)(TCN_STDARGS, jlong ssl, jint mode) { |
- UNREFERENCED(o); |
- UNREFERENCED(ssl); |
- UNREFERENCED(mode); |
- tcn_ThrowException(e, "Not implemented"); |
-} |
+ if (key <= 0) { |
+ key = cert; |
+ } |
-TCN_IMPLEMENT_CALL(void, SSL, freeSSL)(TCN_STDARGS, jlong ssl) { |
- UNREFERENCED(o); |
- UNREFERENCED(ssl); |
- tcn_ThrowException(e, "Not implemented"); |
-} |
+ if (cert <= 0 || key <= 0) { |
+ tcn_Throw(e, "No Certificate file specified or invalid file format"); |
+ goto cleanup; |
+ } |
-TCN_IMPLEMENT_CALL(jlong, SSL, makeNetworkBIO)(TCN_STDARGS, jlong ssl) { |
- UNREFERENCED(o); |
- UNREFERENCED(ssl); |
- tcn_ThrowException(e, "Not implemented"); |
- return 0; |
-} |
+ if ((pkey = load_pem_key_bio(cpassword, key_bio)) == NULL) { |
+ ERR_error_string_n(ERR_get_error(), err, ERR_LEN); |
+ ERR_clear_error(); |
+ tcn_Throw(e, "Unable to load certificate key (%s)",err); |
+ goto cleanup; |
+ } |
+ if ((xcert = load_pem_cert_bio(cpassword, cert_bio)) == NULL) { |
+ ERR_error_string_n(ERR_get_error(), err, ERR_LEN); |
+ ERR_clear_error(); |
+ tcn_Throw(e, "Unable to load certificate (%s) ", err); |
+ goto cleanup; |
+ } |
-TCN_IMPLEMENT_CALL(void, SSL, freeBIO)(TCN_STDARGS, jlong bio) { |
- UNREFERENCED(o); |
- UNREFERENCED(bio); |
- tcn_ThrowException(e, "Not implemented"); |
-} |
+ if (SSL_use_certificate(ssl_, xcert) <= 0) { |
+ ERR_error_string_n(ERR_get_error(), err, ERR_LEN); |
+ ERR_clear_error(); |
+ tcn_Throw(e, "Error setting certificate (%s)", err); |
+ goto cleanup; |
+ } |
+ if (SSL_use_PrivateKey(ssl_, pkey) <= 0) { |
+ ERR_error_string_n(ERR_get_error(), err, ERR_LEN); |
+ ERR_clear_error(); |
+ tcn_Throw(e, "Error setting private key (%s)", err); |
+ goto cleanup; |
+ } |
+ if (SSL_check_private_key(ssl_) <= 0) { |
+ ERR_error_string_n(ERR_get_error(), err, ERR_LEN); |
+ ERR_clear_error(); |
-TCN_IMPLEMENT_CALL(jint, SSL, shutdownSSL)(TCN_STDARGS, jlong ssl) { |
- UNREFERENCED(o); |
- UNREFERENCED(ssl); |
- tcn_ThrowException(e, "Not implemented"); |
- return 0; |
+ tcn_Throw(e, "Private key does not match the certificate public key (%s)", |
+ err); |
+ goto cleanup; |
+ } |
+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 |
} |
-TCN_IMPLEMENT_CALL(jstring, SSL, getCipherForSSL)(TCN_STDARGS, jlong ssl) { |
- UNREFERENCED(o); |
- UNREFERENCED(ssl); |
- tcn_ThrowException(e, "Not implemented"); |
- return NULL; |
-} |
+TCN_IMPLEMENT_CALL(void, SSL, setCertificateChainBio)(TCN_STDARGS, jlong ssl, |
+ jlong chain, |
+ jboolean skipfirst) |
+{ |
+ SSL *ssl_ = J2P(ssl, SSL *); |
+ BIO *b = J2P(chain, BIO *); |
+ char err[ERR_LEN]; |
-TCN_IMPLEMENT_CALL(jint, SSL, isInInit)(TCN_STDARGS, jlong ssl) { |
- UNREFERENCED(o); |
- UNREFERENCED(ssl); |
- tcn_ThrowException(e, "Not implemented"); |
- return 0; |
-} |
+ UNREFERENCED(o); |
+ TCN_ASSERT(ssl_ != NULL); |
+ TCN_ASSERT(b != NULL); |
-TCN_IMPLEMENT_CALL(jint, SSL, doHandshake)(TCN_STDARGS, jlong ssl) { |
- UNREFERENCED(o); |
- UNREFERENCED(ssl); |
- tcn_ThrowException(e, "Not implemented"); |
+ if (SSL_use_certificate_chain_bio(ssl_, b, skipfirst) < 0) { |
+ ERR_error_string_n(ERR_get_error(), err, ERR_LEN); |
+ ERR_clear_error(); |
+ tcn_Throw(e, "Error setting certificate chain (%s)", err); |
+ } |
} |
-TCN_IMPLEMENT_CALL(jstring, SSL, getNextProtoNegotiated)(TCN_STDARGS, jlong ssl) { |
- UNREFERENCED(o); |
- UNREFERENCED(ssl); |
- tcn_ThrowException(e, "Not implemented"); |
- return NULL; |
-} |
+TCN_IMPLEMENT_CALL(long, SSL, parsePrivateKey)(TCN_STDARGS, jlong privateKeyBio, jstring password) |
+{ |
+ EVP_PKEY* pkey = NULL; |
+ BIO *bio = J2P(privateKeyBio, BIO *); |
+ TCN_ALLOC_CSTRING(password); |
+ char err[ERR_LEN]; |
-/*** End Twitter 1:1 API addition ***/ |
+ UNREFERENCED(o); |
-/*** Begin Apple 1:1 API addition ***/ |
+ if (bio == NULL) { |
+ tcn_Throw(e, "Unable to load certificate key"); |
+ goto cleanup; |
+ } |
-TCN_IMPLEMENT_CALL(jstring, SSL, getAlpnSelected)(TCN_STDARGS, jlong ssl) { |
- UNREFERENCED(o); |
- UNREFERENCED(ssl); |
- tcn_ThrowException(e, "Not implemented"); |
- return NULL; |
-} |
+ if ((pkey = load_pem_key_bio(cpassword, bio)) == NULL) { |
+ ERR_error_string_n(ERR_get_error(), err, ERR_LEN); |
+ ERR_clear_error(); |
+ tcn_Throw(e, "Unable to load certificate key (%s)",err); |
+ goto cleanup; |
+ } |
-TCN_IMPLEMENT_CALL(jobjectArray, SSL, getPeerCertChain)(TCN_STDARGS, jlong ssl) |
-{ |
- UNREFERENCED(o); |
- UNREFERENCED(ssl); |
- tcn_ThrowException(e, "Not implemented"); |
- return NULL; |
+cleanup: |
+ TCN_FREE_CSTRING(password); |
+ return P2J(pkey); |
} |
-TCN_IMPLEMENT_CALL(jbyteArray, SSL, getPeerCertificate)(TCN_STDARGS, jlong ssl) |
+TCN_IMPLEMENT_CALL(void, SSL, freePrivateKey)(TCN_STDARGS, jlong privateKey) |
{ |
- UNREFERENCED(o); |
- UNREFERENCED(ssl); |
- tcn_ThrowException(e, "Not implemented"); |
- return NULL; |
+ EVP_PKEY *key = J2P(privateKey, EVP_PKEY *); |
+ UNREFERENCED(o); |
+ EVP_PKEY_free(key); // Safe to call with NULL as well. |
} |
-TCN_IMPLEMENT_CALL(jstring, SSL, getErrorString)(TCN_STDARGS, jlong number) |
+TCN_IMPLEMENT_CALL(long, SSL, parseX509Chain)(TCN_STDARGS, jlong x509ChainBio) |
{ |
- UNREFERENCED(o); |
- tcn_ThrowException(e, "Not implemented"); |
- return NULL; |
-} |
+ BIO *cert_bio = J2P(x509ChainBio, BIO *); |
+ X509* cert = NULL; |
+ STACK_OF(X509) *chain = NULL; |
+ char err[ERR_LEN]; |
+ unsigned long error; |
+ int n = 0; |
-TCN_IMPLEMENT_CALL(jstring, SSL, getVersion)(TCN_STDARGS, jlong ssl) |
-{ |
- UNREFERENCED(o); |
- UNREFERENCED(ssl); |
- tcn_ThrowException(e, "Not implemented"); |
- return NULL; |
-} |
+ UNREFERENCED(o); |
-TCN_IMPLEMENT_CALL(jlong, SSL, getTime)(TCN_STDARGS, jlong ssl) |
-{ |
- UNREFERENCED(o); |
- UNREFERENCED(ssl); |
- tcn_ThrowException(e, "Not implemented"); |
- return 0; |
-} |
+ if (cert_bio == NULL) { |
+ tcn_Throw(e, "No Certificate specified or invalid format"); |
+ goto cleanup; |
+ } |
-TCN_IMPLEMENT_CALL(jlong, SSL, getTimeout)(TCN_STDARGS, jlong ssl) |
-{ |
- UNREFERENCED(o); |
- UNREFERENCED(ssl); |
- tcn_ThrowException(e, "Not implemented"); |
- return 0; |
-} |
+ chain = sk_X509_new_null(); |
+ while ((cert = PEM_read_bio_X509(cert_bio, NULL, NULL, NULL)) != NULL) { |
+ if (sk_X509_push(chain, cert) <= 0) { |
+ tcn_Throw(e, "No Certificate specified or invalid format"); |
+ goto cleanup; |
+ } |
+ cert = NULL; |
+ n++; |
+ } |
-TCN_IMPLEMENT_CALL(jlong, SSL, setTimeout)(TCN_STDARGS, jlong ssl, jlong seconds) |
-{ |
- UNREFERENCED(o); |
- UNREFERENCED(ssl); |
- UNREFERENCED(seconds); |
- tcn_ThrowException(e, "Not implemented"); |
- return 0; |
-} |
+ // ensure that if we have an error its just for EOL. |
+ if ((error = ERR_peek_error()) > 0) { |
+ if (!(ERR_GET_LIB(error) == ERR_LIB_PEM |
+ && ERR_GET_REASON(error) == PEM_R_NO_START_LINE)) { |
-TCN_IMPLEMENT_CALL(void, SSL, setVerify)(TCN_STDARGS, jlong ssl, |
- jint level, jint depth) |
-{ |
- UNREFERENCED(o); |
- UNREFERENCED(ssl); |
- tcn_ThrowException(e, "Not implemented"); |
-} |
+ ERR_error_string_n(ERR_get_error(), err, ERR_LEN); |
+ tcn_Throw(e, "Invalid format (%s)", err); |
+ goto cleanup; |
+ } |
+ ERR_clear_error(); |
+ } |
-TCN_IMPLEMENT_CALL(void, SSL, setOptions)(TCN_STDARGS, jlong ssl, |
- jint opt) |
-{ |
- UNREFERENCED_STDARGS; |
- UNREFERENCED(ssl); |
- UNREFERENCED(opt); |
- tcn_ThrowException(e, "Not implemented"); |
-} |
+ return P2J(chain); |
-TCN_IMPLEMENT_CALL(jint, SSL, getOptions)(TCN_STDARGS, jlong ssl) |
-{ |
- UNREFERENCED_STDARGS; |
- UNREFERENCED(ssl); |
- tcn_ThrowException(e, "Not implemented"); |
- return 0; |
-} |
-TCN_IMPLEMENT_CALL(jobjectArray, SSL, getCiphers)(TCN_STDARGS, jlong ssl) |
-{ |
- UNREFERENCED_STDARGS; |
- UNREFERENCED(ssl); |
- tcn_ThrowException(e, "Not implemented"); |
+cleanup: |
+ ERR_clear_error(); |
+ sk_X509_pop_free(chain, X509_free); |
+ X509_free(cert); |
return 0; |
} |
-TCN_IMPLEMENT_CALL(jboolean, SSL, setCipherSuites)(TCN_STDARGS, jlong ssl, |
- jstring ciphers) |
-{ |
- UNREFERENCED_STDARGS; |
- UNREFERENCED(ssl); |
- UNREFERENCED(ciphers); |
- tcn_ThrowException(e, "Not implemented"); |
- return JNI_FALSE; |
-} |
-TCN_IMPLEMENT_CALL(jbyteArray, SSL, getSessionId)(TCN_STDARGS, jlong ssl) |
-{ |
- UNREFERENCED(o); |
- UNREFERENCED(ssl); |
- tcn_ThrowException(e, "Not implemented"); |
-} |
-TCN_IMPLEMENT_CALL(jint, SSL, getHandshakeCount)(TCN_STDARGS, jlong ssl) |
-{ |
- UNREFERENCED(o); |
- UNREFERENCED(ssl); |
- tcn_ThrowException(e, "Not implemented"); |
-} |
-TCN_IMPLEMENT_CALL(void, SSL, clearError)(TCN_STDARGS) |
+TCN_IMPLEMENT_CALL(void, SSL, freeX509Chain)(TCN_STDARGS, jlong x509Chain) |
{ |
+ STACK_OF(X509) *chain = J2P(x509Chain, STACK_OF(X509) *); |
UNREFERENCED(o); |
- tcn_ThrowException(e, "Not implemented"); |
+ sk_X509_pop_free(chain, X509_free); |
} |
-TCN_IMPLEMENT_CALL(jint, SSL, renegotiate)(TCN_STDARGS, jlong ssl) { |
- UNREFERENCED(o); |
- UNREFERENCED(ssl); |
- tcn_ThrowException(e, "Not implemented"); |
-} |
- |
-TCN_IMPLEMENT_CALL(void, SSL, setState)(TCN_STDARGS, jlong ssl, jint state) { |
- UNREFERENCED(o); |
- UNREFERENCED(ssl); |
- UNREFERENCED(state); |
- tcn_ThrowException(e, "Not implemented"); |
-} |
-/*** End Apple API Additions ***/ |
-#endif |