| /* |
| * 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. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #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_strings.h" |
| #include "apr_portable.h" |
| #include "ssl_private.h" |
| |
| static int ssl_initialized = 0; |
| extern apr_pool_t *tcn_global_pool; |
| |
| ENGINE *tcn_ssl_engine = NULL; |
| void *SSL_temp_keys[SSL_TMP_KEY_MAX]; |
| |
| /* Global reference to the pool used by the dynamic mutexes */ |
| static apr_pool_t *dynlockpool = NULL; |
| |
| /* Dynamic lock structure */ |
| struct CRYPTO_dynlock_value { |
| apr_pool_t *pool; |
| const char* file; |
| int line; |
| 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 |
| */ |
| |
| #define SSL_TMP_KEY_FREE(type, idx) \ |
| if (SSL_temp_keys[idx]) { \ |
| type##_free((type *)SSL_temp_keys[idx]); \ |
| SSL_temp_keys[idx] = NULL; \ |
| } else (void)(0) |
| |
| #define SSL_TMP_KEYS_FREE(type) \ |
| SSL_TMP_KEY_FREE(type, SSL_TMP_KEY_##type##_512); \ |
| SSL_TMP_KEY_FREE(type, SSL_TMP_KEY_##type##_1024); \ |
| 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_DH(bits) \ |
| ssl_tmp_key_init_dh(bits, SSL_TMP_KEY_DH_##bits) |
| |
| #define SSL_TMP_KEYS_INIT(R) \ |
| R |= SSL_TMP_KEY_INIT_DH(512); \ |
| R |= SSL_TMP_KEY_INIT_DH(1024); \ |
| R |= SSL_TMP_KEY_INIT_DH(2048); \ |
| R |= SSL_TMP_KEY_INIT_DH(4096) |
| |
| /* |
| * supported_ssl_opts is a bitmask that contains all supported SSL_OP_* |
| * options at compile-time. This is used in hasOp to determine which |
| * SSL_OP_* options are available at runtime. |
| * |
| * Note that at least up through OpenSSL 0.9.8o, checking SSL_OP_ALL will |
| * return JNI_FALSE because SSL_OP_ALL is a mask that covers all bug |
| * workarounds for OpenSSL including future workarounds that are defined |
| * to be in the least-significant 3 nibbles of the SSL_OP_* bit space. |
| * |
| * This implementation has chosen NOT to simply set all those lower bits |
| * so that the return value for SSL_OP_FUTURE_WORKAROUND will only be |
| * reported by versions that actually support that specific workaround. |
| */ |
| static const jint supported_ssl_opts = 0 |
| /* |
| Specifically skip SSL_OP_ALL |
| #ifdef SSL_OP_ALL |
| | SSL_OP_ALL |
| #endif |
| */ |
| #ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION |
| | SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION |
| #endif |
| |
| #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE |
| | SSL_OP_CIPHER_SERVER_PREFERENCE |
| #endif |
| |
| #ifdef SSL_OP_CRYPTOPRO_TLSEXT_BUG |
| | SSL_OP_CRYPTOPRO_TLSEXT_BUG |
| #endif |
| |
| #ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS |
| | SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS |
| #endif |
| |
| #ifdef SSL_OP_LEGACY_SERVER_CONNECT |
| | SSL_OP_LEGACY_SERVER_CONNECT |
| #endif |
| |
| #ifdef SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER |
| | SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER |
| #endif |
| |
| #ifdef SSL_OP_MICROSOFT_SESS_ID_BUG |
| | SSL_OP_MICROSOFT_SESS_ID_BUG |
| #endif |
| |
| #ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING |
| | SSL_OP_MSIE_SSLV2_RSA_PADDING |
| #endif |
| |
| #ifdef SSL_OP_NETSCAPE_CA_DN_BUG |
| | SSL_OP_NETSCAPE_CA_DN_BUG |
| #endif |
| |
| #ifdef SSL_OP_NETSCAPE_CHALLENGE_BUG |
| | SSL_OP_NETSCAPE_CHALLENGE_BUG |
| #endif |
| |
| #ifdef SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG |
| | SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG |
| #endif |
| |
| #ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG |
| | SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG |
| #endif |
| |
| #ifdef SSL_OP_NO_COMPRESSION |
| | SSL_OP_NO_COMPRESSION |
| #endif |
| |
| #ifdef SSL_OP_NO_QUERY_MTU |
| | SSL_OP_NO_QUERY_MTU |
| #endif |
| |
| #ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION |
| | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION |
| #endif |
| |
| #ifdef SSL_OP_NO_SSLv2 |
| | SSL_OP_NO_SSLv2 |
| #endif |
| |
| #ifdef SSL_OP_NO_SSLv3 |
| | SSL_OP_NO_SSLv3 |
| #endif |
| |
| #ifdef SSL_OP_NO_TICKET |
| | SSL_OP_NO_TICKET |
| #endif |
| |
| #ifdef SSL_OP_NO_TLSv1 |
| | SSL_OP_NO_TLSv1 |
| #endif |
| |
| #ifdef SSL_OP_PKCS1_CHECK_1 |
| | SSL_OP_PKCS1_CHECK_1 |
| #endif |
| |
| #ifdef SSL_OP_PKCS1_CHECK_2 |
| | SSL_OP_PKCS1_CHECK_2 |
| #endif |
| |
| #ifdef SSL_OP_NO_TLSv1_1 |
| | SSL_OP_NO_TLSv1_1 |
| #endif |
| |
| #ifdef SSL_OP_NO_TLSv1_2 |
| | SSL_OP_NO_TLSv1_2 |
| #endif |
| |
| #ifdef SSL_OP_SINGLE_DH_USE |
| | SSL_OP_SINGLE_DH_USE |
| #endif |
| |
| #ifdef SSL_OP_SINGLE_ECDH_USE |
| | SSL_OP_SINGLE_ECDH_USE |
| #endif |
| |
| #ifdef SSL_OP_SSLEAY_080_CLIENT_DH_BUG |
| | SSL_OP_SSLEAY_080_CLIENT_DH_BUG |
| #endif |
| |
| #ifdef SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG |
| | SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG |
| #endif |
| |
| #ifdef SSL_OP_TLS_BLOCK_PADDING_BUG |
| | SSL_OP_TLS_BLOCK_PADDING_BUG |
| #endif |
| |
| #ifdef SSL_OP_TLS_D5_BUG |
| | SSL_OP_TLS_D5_BUG |
| #endif |
| |
| #ifdef SSL_OP_TLS_ROLLBACK_BUG |
| | SSL_OP_TLS_ROLLBACK_BUG |
| #endif |
| | 0; |
| |
| 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 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 |
| |
| memcpy(out, bioUserData->buffer, (size_t) readAmount); |
| bioUserData->bufferLength -= readAmount; |
| bioUserData->buffer += readAmount; // Pointer arithmetic based on char* type |
| |
| 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; |
| |
| BIO_set_data(bio, bioUserData); |
| |
| // 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 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; |
| } |
| |
| 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 |
| 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 OpenSSL_version_num(); |
| } |
| |
| TCN_IMPLEMENT_CALL(jstring, SSL, versionString)(TCN_STDARGS) |
| { |
| UNREFERENCED(o); |
| return AJP_TO_JSTRING(OpenSSL_version(OPENSSL_VERSION)); |
| } |
| |
| /* |
| * the various processing hooks |
| */ |
| static apr_status_t ssl_init_cleanup(void *data) |
| { |
| UNREFERENCED(data); |
| |
| if (!ssl_initialized) |
| return APR_SUCCESS; |
| ssl_initialized = 0; |
| |
| SSL_TMP_KEYS_FREE(DH); |
| /* |
| * Try to kill the internals of the SSL library. |
| */ |
| #if OPENSSL_VERSION_NUMBER >= 0x00907001 && !defined(OPENSSL_IS_BORINGSSL) |
| /* Corresponds to OPENSSL_load_builtin_modules(): |
| * XXX: borrowed from apps.h, but why not CONF_modules_free() |
| * which also invokes CONF_modules_finish()? |
| */ |
| CONF_modules_unload(1); |
| #endif |
| /* Corresponds to SSL_library_init: */ |
| EVP_cleanup(); |
| #if HAVE_ENGINE_LOAD_BUILTIN_ENGINES |
| ENGINE_cleanup(); |
| #endif |
| #if OPENSSL_VERSION_NUMBER >= 0x00907001 |
| CRYPTO_cleanup_all_ex_data(); |
| #endif |
| #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 |
| * variable abuse in OpenSSL. */ |
| |
| /* |
| * TODO: determine somewhere we can safely shove out diagnostics |
| * (when enabled) at this late stage in the game: |
| * CRYPTO_mem_leaks_fp(stderr); |
| */ |
| return APR_SUCCESS; |
| } |
| |
| #ifndef OPENSSL_NO_ENGINE |
| /* Try to load an engine in a shareable library */ |
| static ENGINE *ssl_try_load_engine(const char *engine) |
| { |
| ENGINE *e = ENGINE_by_id("dynamic"); |
| if (e) { |
| if (!ENGINE_ctrl_cmd_string(e, "SO_PATH", engine, 0) |
| || !ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0)) { |
| ENGINE_free(e); |
| e = NULL; |
| } |
| } |
| return e; |
| } |
| #endif |
| |
| /* |
| * To ensure thread-safetyness in OpenSSL |
| */ |
| |
| static apr_thread_mutex_t **ssl_lock_cs; |
| static int ssl_lock_num_locks; |
| |
| static void ssl_thread_lock(int mode, int type, |
| const char *file, int line) |
| { |
| UNREFERENCED(file); |
| UNREFERENCED(line); |
| if (type < ssl_lock_num_locks) { |
| if (mode & CRYPTO_LOCK) { |
| apr_thread_mutex_lock(ssl_lock_cs[type]); |
| } |
| else { |
| apr_thread_mutex_unlock(ssl_lock_cs[type]); |
| } |
| } |
| } |
| |
| static unsigned long ssl_thread_id(void) |
| { |
| /* OpenSSL needs this to return an unsigned long. On OS/390, the pthread |
| * id is a structure twice that big. Use the TCB pointer instead as a |
| * unique unsigned long. |
| */ |
| #ifdef __MVS__ |
| struct PSA { |
| char unmapped[540]; |
| unsigned long PSATOLD; |
| } *psaptr = 0; |
| |
| return psaptr->PSATOLD; |
| #elif defined(WIN32) |
| return (unsigned long)GetCurrentThreadId(); |
| #else |
| return (unsigned long)(apr_os_thread_current()); |
| #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_THREADID_set_callback(NULL); |
| CRYPTO_set_dynlock_create_callback(NULL); |
| CRYPTO_set_dynlock_lock_callback(NULL); |
| CRYPTO_set_dynlock_destroy_callback(NULL); |
| |
| dynlockpool = NULL; |
| |
| /* Let the registered mutex cleanups do their own thing |
| */ |
| return APR_SUCCESS; |
| } |
| |
| /* |
| * Dynamic lock creation callback |
| */ |
| static struct CRYPTO_dynlock_value *ssl_dyn_create_function(const char *file, |
| int line) |
| { |
| struct CRYPTO_dynlock_value *value; |
| apr_pool_t *p; |
| apr_status_t rv; |
| |
| /* |
| * We need a pool to allocate our mutex. Since we can't clear |
| * allocated memory from a pool, create a subpool that we can blow |
| * away in the destruction callback. |
| */ |
| rv = apr_pool_create(&p, dynlockpool); |
| if (rv != APR_SUCCESS) { |
| /* TODO log that fprintf(stderr, "Failed to create subpool for dynamic lock"); */ |
| return NULL; |
| } |
| |
| value = (struct CRYPTO_dynlock_value *)apr_palloc(p, |
| sizeof(struct CRYPTO_dynlock_value)); |
| if (!value) { |
| /* TODO log that fprintf(stderr, "Failed to allocate dynamic lock structure"); */ |
| return NULL; |
| } |
| |
| value->pool = p; |
| /* Keep our own copy of the place from which we were created, |
| using our own pool. */ |
| value->file = apr_pstrdup(p, file); |
| value->line = line; |
| rv = apr_thread_mutex_create(&(value->mutex), APR_THREAD_MUTEX_DEFAULT, |
| p); |
| if (rv != APR_SUCCESS) { |
| /* TODO log that fprintf(stderr, "Failed to create thread mutex for dynamic lock"); */ |
| apr_pool_destroy(p); |
| return NULL; |
| } |
| return value; |
| } |
| |
| /* |
| * Dynamic locking and unlocking function |
| */ |
| 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); |
| } |
| else { |
| apr_thread_mutex_unlock(l->mutex); |
| } |
| } |
| |
| /* |
| * Dynamic lock destruction callback |
| */ |
| static void ssl_dyn_destroy_function(struct CRYPTO_dynlock_value *l, |
| const char *file, int line) |
| { |
| apr_status_t rv; |
| rv = apr_thread_mutex_destroy(l->mutex); |
| if (rv != APR_SUCCESS) { |
| /* TODO log that fprintf(stderr, "Failed to destroy mutex for dynamic lock %s:%d", l->file, l->line); */ |
| } |
| |
| /* Trust that whomever owned the CRYPTO_dynlock_value we were |
| * passed has no future use for it... |
| */ |
| apr_pool_destroy(l->pool); |
| } |
| |
| static void ssl_thread_setup(apr_pool_t *p) |
| { |
| int i; |
| |
| ssl_lock_num_locks = CRYPTO_num_locks(); |
| ssl_lock_cs = apr_palloc(p, ssl_lock_num_locks * sizeof(*ssl_lock_cs)); |
| |
| for (i = 0; i < ssl_lock_num_locks; i++) { |
| apr_thread_mutex_create(&(ssl_lock_cs[i]), |
| APR_THREAD_MUTEX_DEFAULT, p); |
| } |
| |
| 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 |
| * convenience. |
| */ |
| dynlockpool = p; |
| CRYPTO_set_dynlock_create_callback(ssl_dyn_create_function); |
| CRYPTO_set_dynlock_lock_callback(ssl_dyn_lock_function); |
| CRYPTO_set_dynlock_destroy_callback(ssl_dyn_destroy_function); |
| |
| apr_pool_cleanup_register(p, NULL, ssl_thread_cleanup, |
| apr_pool_cleanup_null); |
| } |
| |
| TCN_IMPLEMENT_CALL(jint, SSL, initialize)(TCN_STDARGS, jstring engine) |
| { |
| int r = 0; |
| |
| TCN_ALLOC_CSTRING(engine); |
| |
| UNREFERENCED(o); |
| if (!tcn_global_pool) { |
| TCN_FREE_CSTRING(engine); |
| tcn_ThrowAPRException(e, APR_EINVAL); |
| return (jint)APR_EINVAL; |
| } |
| /* Check if already initialized */ |
| if (ssl_initialized++) { |
| TCN_FREE_CSTRING(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 |
| |
| #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. |
| */ |
| OPENSSL_malloc_init(); |
| #endif |
| |
| ERR_load_crypto_strings(); |
| SSL_load_error_strings(); |
| SSL_library_init(); |
| OpenSSL_add_all_algorithms(); |
| #if HAVE_ENGINE_LOAD_BUILTIN_ENGINES |
| ENGINE_load_builtin_engines(); |
| #endif |
| #if OPENSSL_VERSION_NUMBER >= 0x00907001 |
| OPENSSL_load_builtin_modules(); |
| #endif |
| |
| /* Initialize thread support */ |
| ssl_thread_setup(tcn_global_pool); |
| |
| #ifndef OPENSSL_NO_ENGINE |
| if (J2S(engine)) { |
| ENGINE *ee = NULL; |
| apr_status_t err = APR_SUCCESS; |
| if(strcmp(J2S(engine), "auto") == 0) { |
| ENGINE_register_all_complete(); |
| } |
| else { |
| if ((ee = ENGINE_by_id(J2S(engine))) == NULL |
| && (ee = ssl_try_load_engine(J2S(engine))) == NULL) |
| err = APR_ENOTIMPL; |
| else { |
| #ifdef ENGINE_CTRL_CHIL_SET_FORKCHECK |
| if (strcmp(J2S(engine), "chil") == 0) |
| ENGINE_ctrl(ee, ENGINE_CTRL_CHIL_SET_FORKCHECK, 1, 0, 0); |
| #endif |
| if (!ENGINE_set_default(ee, ENGINE_METHOD_ALL)) |
| err = APR_ENOTIMPL; |
| } |
| /* Free our "structural" reference. */ |
| if (ee) |
| ENGINE_free(ee); |
| } |
| if (err != APR_SUCCESS) { |
| TCN_FREE_CSTRING(engine); |
| ssl_init_cleanup(NULL); |
| tcn_ThrowAPRException(e, err); |
| return (jint)err; |
| } |
| tcn_ssl_engine = ee; |
| } |
| #endif |
| |
| // 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); |
| return APR_ENOTIMPL; |
| } |
| /* |
| * Let us cleanup the ssl library when the library is unloaded |
| */ |
| apr_pool_cleanup_register(tcn_global_pool, NULL, |
| ssl_init_cleanup, |
| apr_pool_cleanup_null); |
| TCN_FREE_CSTRING(engine); |
| |
| return (jint)APR_SUCCESS; |
| } |
| |
| TCN_IMPLEMENT_CALL(jlong, SSL, newMemBIO)(TCN_STDARGS) |
| { |
| BIO *bio = NULL; |
| |
| UNREFERENCED(o); |
| |
| // 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 0; |
| } |
| return P2J(bio); |
| } |
| |
| TCN_IMPLEMENT_CALL(jstring, SSL, getLastError)(TCN_STDARGS) |
| { |
| char buf[ERR_LEN]; |
| UNREFERENCED(o); |
| ERR_error_string(ERR_get_error(), buf); |
| return tcn_new_string(e, buf); |
| } |
| |
| TCN_IMPLEMENT_CALL(jboolean, SSL, hasOp)(TCN_STDARGS, jint op) |
| { |
| return op == (op & supported_ssl_opts) ? JNI_TRUE : JNI_FALSE; |
| } |
| |
| /*** Begin Twitter 1:1 API addition ***/ |
| TCN_IMPLEMENT_CALL(jint, SSL, getLastErrorNumber)(TCN_STDARGS) { |
| UNREFERENCED_STDARGS; |
| return ERR_get_error(); |
| } |
| |
| static void ssl_info_callback(const SSL *ssl, int where, int ret) { |
| int *handshakeCount = NULL; |
| if (0 != (where & SSL_CB_HANDSHAKE_START)) { |
| handshakeCount = (int*) SSL_get_app_data3((SSL*) ssl); |
| if (handshakeCount != NULL) { |
| ++(*handshakeCount); |
| } |
| } |
| } |
| |
| TCN_IMPLEMENT_CALL(jlong /* SSL * */, SSL, newSSL)(TCN_STDARGS, |
| jlong ctx /* tcn_ssl_ctxt_t * */, |
| jboolean server) { |
| SSL *ssl = NULL; |
| int *handshakeCount = NULL; |
| tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); |
| |
| if (c == NULL) { |
| tcn_ThrowException(e, "ssl ctx is null"); |
| return 0; |
| } |
| if (c->ctx == NULL) { |
| tcn_ThrowException(e, "ctx is null"); |
| return 0; |
| } |
| |
| UNREFERENCED_STDARGS; |
| |
| 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); |
| |
| // Add callback to keep track of handshakes. |
| SSL_CTX_set_info_callback(c->ctx, ssl_info_callback); |
| |
| if (server) { |
| SSL_set_accept_state(ssl); |
| } else { |
| SSL_set_connect_state(ssl); |
| } |
| |
| return P2J(ssl); |
| } |
| |
| TCN_IMPLEMENT_CALL(jint, SSL, getError)(TCN_STDARGS, |
| jlong ssl /* SSL * */, |
| jint ret) { |
| SSL *ssl_ = J2P(ssl, SSL *); |
| |
| if (ssl_ == NULL) { |
| tcn_ThrowException(e, "ssl is null"); |
| return 0; |
| } |
| |
| UNREFERENCED_STDARGS; |
| |
| return SSL_get_error(ssl_, ret); |
| } |
| |
| // Write wlen bytes from wbuf into bio |
| 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 (wbuf == NULL) { |
| tcn_ThrowException(e, "wbuf is null"); |
| return 0; |
| } |
| |
| UNREFERENCED_STDARGS; |
| |
| return BIO_write(bio, wbuf, wlen); |
| } |
| |
| 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; |
| } |
| |
| 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; |
| } |
| |
| 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 == 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) |
| TCN_IMPLEMENT_CALL(jint /* status */, SSL, writeToSSL)(TCN_STDARGS, |
| jlong ssl /* SSL * */, |
| jlong wbuf /* char * */, |
| jint wlen /* sizeof(wbuf) */) { |
| SSL *ssl_ = J2P(ssl, SSL *); |
| void *w = J2P(wbuf, void *); |
| |
| if (ssl_ == NULL) { |
| tcn_ThrowException(e, "ssl is null"); |
| return 0; |
| } |
| if (w == NULL) { |
| tcn_ThrowException(e, "wbuf is null"); |
| return 0; |
| } |
| |
| UNREFERENCED_STDARGS; |
| |
| return SSL_write(ssl_, w, wlen); |
| } |
| |
| // Read up to rlen bytes of application data from the given SSL BIO (decrypt) |
| TCN_IMPLEMENT_CALL(jint /* status */, SSL, readFromSSL)(TCN_STDARGS, |
| jlong ssl /* SSL * */, |
| jlong rbuf /* char * */, |
| jint rlen /* sizeof(rbuf) - 1 */) { |
| SSL *ssl_ = J2P(ssl, SSL *); |
| void *r = J2P(rbuf, void *); |
| |
| if (ssl_ == NULL) { |
| tcn_ThrowException(e, "ssl is null"); |
| return 0; |
| } |
| if (r == NULL) { |
| tcn_ThrowException(e, "rbuf is null"); |
| return 0; |
| } |
| |
| UNREFERENCED_STDARGS; |
| |
| return SSL_read(ssl_, r, rlen); |
| } |
| |
| // Get the shutdown status of the engine |
| TCN_IMPLEMENT_CALL(jint /* status */, SSL, getShutdown)(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_get_shutdown(ssl_); |
| } |
| |
| // Called when the peer closes the connection |
| TCN_IMPLEMENT_CALL(void, SSL, setShutdown)(TCN_STDARGS, |
| jlong ssl /* SSL * */, |
| jint mode) { |
| SSL *ssl_ = J2P(ssl, SSL *); |
| |
| if (ssl_ == NULL) { |
| tcn_ThrowException(e, "ssl is null"); |
| return; |
| } |
| |
| UNREFERENCED_STDARGS; |
| |
| SSL_set_shutdown(ssl_, mode); |
| } |
| |
| // Free the SSL * and its associated internal BIO |
| 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) { |
| 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_); |
| } |
| |
| 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 (nonApplicationBufferSize <= 0) { |
| tcn_ThrowException(e, "nonApplicationBufferSize <= 0"); |
| return 0; |
| } |
| |
| 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_, bio, bio); |
| |
| return P2J(bio); |
| } |
| |
| // Free a BIO * (typically, the network BIO) |
| TCN_IMPLEMENT_CALL(void, SSL, freeBIO)(TCN_STDARGS, |
| jlong bio /* BIO * */) { |
| BIO *bio_ = J2P(bio, BIO *); |
| |
| UNREFERENCED_STDARGS; |
| |
| if (bio_ != NULL) { |
| BIO_free(bio_); |
| } |
| } |
| |
| // Send CLOSE_NOTIFY to peer |
| TCN_IMPLEMENT_CALL(jint /* status */, SSL, shutdownSSL)(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_shutdown(ssl_); |
| } |
| |
| // Read which cipher was negotiated for the given SSL *. |
| TCN_IMPLEMENT_CALL(jstring, SSL, getCipherForSSL)(TCN_STDARGS, |
| jlong ssl /* SSL * */) |
| { |
| SSL *ssl_ = J2P(ssl, SSL *); |
| |
| if (ssl_ == NULL) { |
| tcn_ThrowException(e, "ssl is null"); |
| return NULL; |
| } |
| |
| UNREFERENCED_STDARGS; |
| |
| return AJP_TO_JSTRING(SSL_get_cipher(ssl_)); |
| } |
| |
| // Read which protocol was negotiated for the given SSL *. |
| TCN_IMPLEMENT_CALL(jstring, SSL, getVersion)(TCN_STDARGS, |
| jlong ssl /* SSL * */) |
| { |
| SSL *ssl_ = J2P(ssl, SSL *); |
| |
| if (ssl_ == NULL) { |
| tcn_ThrowException(e, "ssl is null"); |
| return NULL; |
| } |
| |
| UNREFERENCED_STDARGS; |
| |
| return AJP_TO_JSTRING(SSL_get_version(ssl_)); |
| } |
| |
| // Is the handshake over yet? |
| TCN_IMPLEMENT_CALL(jint, SSL, isInInit)(TCN_STDARGS, |
| jlong ssl /* SSL * */) { |
| SSL *ssl_ = J2P(ssl, SSL *); |
| |
| if (ssl_ == NULL) { |
| tcn_ThrowException(e, "ssl is null"); |
| return 0; |
| } |
| |
| UNREFERENCED(o); |
| |
| return SSL_in_init(ssl_); |
| } |
| |
| TCN_IMPLEMENT_CALL(jint, SSL, doHandshake)(TCN_STDARGS, |
| jlong ssl /* SSL * */) { |
| SSL *ssl_ = J2P(ssl, SSL *); |
| |
| if (ssl_ == NULL) { |
| tcn_ThrowException(e, "ssl is null"); |
| return 0; |
| } |
| |
| UNREFERENCED(o); |
| |
| return SSL_do_handshake(ssl_); |
| } |
| |
| // Read which protocol was negotiated for the given SSL *. |
| TCN_IMPLEMENT_CALL(jstring, SSL, getNextProtoNegotiated)(TCN_STDARGS, |
| jlong ssl /* SSL * */) { |
| SSL *ssl_ = J2P(ssl, SSL *); |
| const unsigned char *proto; |
| unsigned int proto_len; |
| |
| if (ssl_ == NULL) { |
| tcn_ThrowException(e, "ssl is null"); |
| return NULL; |
| } |
| |
| UNREFERENCED(o); |
| |
| SSL_get0_next_proto_negotiated(ssl_, &proto, &proto_len); |
| return tcn_new_stringn(e, (char*) proto, proto_len); |
| } |
| |
| /*** End Twitter API Additions ***/ |
| |
| /*** Apple API Additions ***/ |
| |
| TCN_IMPLEMENT_CALL(jstring, SSL, getAlpnSelected)(TCN_STDARGS, |
| jlong ssl /* SSL * */) { |
| // Use weak linking with GCC as this will alow us to run the same packaged version with multiple |
| // version of openssl. |
| #if defined(__GNUC__) || defined(__GNUG__) |
| if (!SSL_get0_alpn_selected) { |
| UNREFERENCED(o); |
| UNREFERENCED(ssl); |
| return NULL; |
| } |
| #endif |
| |
| // We can only support it when either use openssl version >= 1.0.2 or GCC as this way we can use weak linking |
| #if OPENSSL_VERSION_NUMBER >= 0x10002000L || defined(__GNUC__) || defined(__GNUG__) |
| SSL *ssl_ = J2P(ssl, SSL *); |
| const unsigned char *proto; |
| unsigned int proto_len; |
| |
| if (ssl_ == NULL) { |
| tcn_ThrowException(e, "ssl is null"); |
| return NULL; |
| } |
| |
| UNREFERENCED(o); |
| |
| SSL_get0_alpn_selected(ssl_, &proto, &proto_len); |
| return tcn_new_stringn(e, (char*) proto, proto_len); |
| #else |
| UNREFERENCED(o); |
| UNREFERENCED(ssl); |
| return NULL; |
| #endif |
| } |
| |
| TCN_IMPLEMENT_CALL(jobjectArray, SSL, getPeerCertChain)(TCN_STDARGS, |
| jlong ssl /* SSL * */) |
| { |
| STACK_OF(X509) *sk; |
| int len; |
| int i; |
| X509 *cert; |
| int length; |
| unsigned char *buf; |
| jobjectArray array; |
| jbyteArray bArray; |
| jclass byteArrayClass = tcn_get_byte_array_class(); |
| |
| SSL *ssl_ = J2P(ssl, SSL *); |
| |
| if (ssl_ == NULL) { |
| tcn_ThrowException(e, "ssl is null"); |
| return NULL; |
| } |
| |
| UNREFERENCED(o); |
| |
| // Get a stack of all certs in the chain. |
| sk = SSL_get_peer_cert_chain(ssl_); |
| |
| 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; |
| } |
| // Create the byte[][]Â array that holds all the certs |
| array = (*e)->NewObjectArray(e, len, byteArrayClass, NULL); |
| |
| for(i = 0; i < len; i++) { |
| cert = sk_X509_value(sk, i); |
| |
| buf = NULL; |
| length = i2d_X509(cert, &buf); |
| if (length < 0) { |
| if (buf != NULL) { |
| 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); |
| |
| // 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); |
| } |
| return array; |
| } |
| |
| TCN_IMPLEMENT_CALL(jbyteArray, SSL, getPeerCertificate)(TCN_STDARGS, |
| jlong ssl /* SSL * */) |
| { |
| X509 *cert; |
| int length; |
| unsigned char *buf = NULL; |
| jbyteArray bArray; |
| |
| SSL *ssl_ = J2P(ssl, SSL *); |
| |
| if (ssl_ == NULL) { |
| tcn_ThrowException(e, "ssl is null"); |
| return NULL; |
| } |
| |
| UNREFERENCED(o); |
| |
| // Get a stack of all certs in the chain |
| cert = SSL_get_peer_certificate(ssl_); |
| if (cert == NULL) { |
| return NULL; |
| } |
| |
| length = i2d_X509(cert, &buf); |
| |
| bArray = (*e)->NewByteArray(e, length); |
| (*e)->SetByteArrayRegion(e, bArray, 0, length, (jbyte*) buf); |
| |
| // We need to free the cert as the reference count is incremented by one and it is not destroyed when the |
| // session is freed. |
| // See https://www.openssl.org/docs/ssl/SSL_get_peer_certificate.html |
| X509_free(cert); |
| |
| OPENSSL_free(buf); |
| |
| return bArray; |
| } |
| |
| TCN_IMPLEMENT_CALL(jstring, SSL, getErrorString)(TCN_STDARGS, jlong number) |
| { |
| char buf[ERR_LEN]; |
| UNREFERENCED(o); |
| ERR_error_string(number, buf); |
| return tcn_new_string(e, buf); |
| } |
| |
| TCN_IMPLEMENT_CALL(jlong, SSL, getTime)(TCN_STDARGS, jlong ssl) |
| { |
| SSL *ssl_ = J2P(ssl, SSL *); |
| SSL_SESSION *session = NULL; |
| |
| if (ssl_ == NULL) { |
| tcn_ThrowException(e, "ssl is null"); |
| return 0; |
| } |
| |
| session = SSL_get_session(ssl_); |
| if (session == NULL) { |
| // BoringSSL does not protect against a NULL session. OpenSSL |
| // returns 0 if the session is NULL, so do that here. |
| return 0; |
| } |
| |
| UNREFERENCED(o); |
| |
| return SSL_get_time(session); |
| } |
| |
| |
| TCN_IMPLEMENT_CALL(jlong, SSL, getTimeout)(TCN_STDARGS, jlong ssl) |
| { |
| SSL *ssl_ = J2P(ssl, SSL *); |
| SSL_SESSION *session = NULL; |
| |
| if (ssl_ == NULL) { |
| tcn_ThrowException(e, "ssl is null"); |
| return 0; |
| } |
| |
| session = SSL_get_session(ssl_); |
| if (session == NULL) { |
| // BoringSSL does not protect against a NULL session. OpenSSL |
| // returns 0 if the session is NULL, so do that here. |
| return 0; |
| } |
| |
| UNREFERENCED(o); |
| |
| return SSL_get_timeout(session); |
| } |
| |
| |
| TCN_IMPLEMENT_CALL(jlong, SSL, setTimeout)(TCN_STDARGS, jlong ssl, jlong seconds) |
| { |
| SSL *ssl_ = J2P(ssl, SSL *); |
| SSL_SESSION *session = NULL; |
| |
| if (ssl_ == NULL) { |
| tcn_ThrowException(e, "ssl is null"); |
| return 0; |
| } |
| |
| session = SSL_get_session(ssl_); |
| if (session == NULL) { |
| // BoringSSL does not protect against a NULL session. OpenSSL |
| // returns 0 if the session is NULL, so do that here. |
| return 0; |
| } |
| |
| UNREFERENCED(o); |
| |
| return SSL_set_timeout(session, seconds); |
| } |
| |
| |
| TCN_IMPLEMENT_CALL(void, SSL, setVerify)(TCN_STDARGS, jlong ssl, jint level, jint depth) |
| { |
| tcn_ssl_verify_config_t* verify_config; |
| tcn_ssl_ctxt_t* c; |
| SSL *ssl_ = J2P(ssl, SSL *); |
| |
| if (ssl_ == NULL) { |
| tcn_ThrowException(e, "ssl is null"); |
| return; |
| } |
| |
| c = SSL_get_app_data2(ssl_); |
| verify_config = SSL_get_app_data4(ssl_); |
| |
| UNREFERENCED(o); |
| TCN_ASSERT(c != NULL); |
| TCN_ASSERT(verify_config != NULL); |
| |
| // 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); |
| } |
| |
| // 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, |
| jint opt) |
| { |
| SSL *ssl_ = J2P(ssl, SSL *); |
| |
| if (ssl_ == NULL) { |
| tcn_ThrowException(e, "ssl is null"); |
| return; |
| } |
| |
| UNREFERENCED_STDARGS; |
| |
| 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 *); |
| |
| if (ssl_ == NULL) { |
| tcn_ThrowException(e, "ssl is null"); |
| return 0; |
| } |
| |
| UNREFERENCED_STDARGS; |
| |
| return SSL_get_options(ssl_); |
| } |
| |
| TCN_IMPLEMENT_CALL(jobjectArray, SSL, getCiphers)(TCN_STDARGS, jlong ssl) |
| { |
| STACK_OF(SSL_CIPHER) *sk; |
| int len; |
| jobjectArray array; |
| const SSL_CIPHER *cipher; |
| const char *name; |
| int i; |
| jstring c_name; |
| SSL *ssl_ = J2P(ssl, SSL *); |
| |
| if (ssl_ == NULL) { |
| tcn_ThrowException(e, "ssl is null"); |
| return NULL; |
| } |
| |
| UNREFERENCED_STDARGS; |
| |
| sk = SSL_get_ciphers(ssl_); |
| 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. |
| return NULL; |
| } |
| |
| // Create the byte[][]Â array that holds all the certs |
| array = (*e)->NewObjectArray(e, len, tcn_get_string_class(), NULL); |
| |
| for (i = 0; i < len; i++) { |
| cipher = sk_SSL_CIPHER_value(sk, i); |
| name = SSL_CIPHER_get_name(cipher); |
| |
| c_name = (*e)->NewStringUTF(e, name); |
| (*e)->SetObjectArrayElement(e, array, i, c_name); |
| } |
| return array; |
| } |
| |
| TCN_IMPLEMENT_CALL(jboolean, SSL, setCipherSuites)(TCN_STDARGS, jlong ssl, |
| jstring ciphers) |
| { |
| jboolean rv = JNI_TRUE; |
| TCN_ALLOC_CSTRING(ciphers); |
| SSL *ssl_ = J2P(ssl, SSL *); |
| |
| if (ssl_ == NULL) { |
| tcn_ThrowException(e, "ssl is null"); |
| return JNI_FALSE; |
| } |
| |
| UNREFERENCED(o); |
| |
| if (!J2S(ciphers)) { |
| return JNI_FALSE; |
| } |
| |
| if (!SSL_set_cipher_list(ssl_, J2S(ciphers))) { |
| 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; |
| } |
| |
| TCN_FREE_CSTRING(ciphers); |
| return rv; |
| } |
| |
| TCN_IMPLEMENT_CALL(jbyteArray, SSL, getSessionId)(TCN_STDARGS, jlong ssl) |
| { |
| |
| unsigned int len; |
| const unsigned char *session_id; |
| SSL_SESSION *session; |
| jbyteArray bArray; |
| SSL *ssl_ = J2P(ssl, SSL *); |
| |
| if (ssl_ == NULL) { |
| tcn_ThrowException(e, "ssl is null"); |
| return NULL; |
| } |
| |
| UNREFERENCED(o); |
| |
| session = SSL_get_session(ssl_); |
| if (session == NULL) { |
| return NULL; |
| } |
| |
| session_id = SSL_SESSION_get_id(session, &len); |
| if (len == 0 || session_id == NULL) { |
| return NULL; |
| } |
| |
| bArray = (*e)->NewByteArray(e, len); |
| (*e)->SetByteArrayRegion(e, bArray, 0, len, (jbyte*) session_id); |
| return bArray; |
| } |
| |
| TCN_IMPLEMENT_CALL(jint, SSL, getHandshakeCount)(TCN_STDARGS, jlong ssl) |
| { |
| int *handshakeCount = NULL; |
| SSL *ssl_ = J2P(ssl, SSL *); |
| |
| if (ssl_ == NULL) { |
| tcn_ThrowException(e, "ssl is null"); |
| return -1; |
| } |
| |
| UNREFERENCED(o); |
| |
| handshakeCount = SSL_get_app_data3(ssl_); |
| return handshakeCount != NULL ? *handshakeCount : 0; |
| } |
| |
| |
| TCN_IMPLEMENT_CALL(void, SSL, clearError)(TCN_STDARGS) |
| { |
| UNREFERENCED(o); |
| ERR_clear_error(); |
| } |
| |
| TCN_IMPLEMENT_CALL(jint, SSL, renegotiate)(TCN_STDARGS, |
| jlong ssl /* SSL * */) { |
| SSL *ssl_ = J2P(ssl, SSL *); |
| |
| if (ssl_ == NULL) { |
| tcn_ThrowException(e, "ssl is null"); |
| return 0; |
| } |
| |
| UNREFERENCED(o); |
| |
| return SSL_renegotiate(ssl_); |
| } |
| |
| TCN_IMPLEMENT_CALL(void, SSL, setState)(TCN_STDARGS, |
| jlong ssl, /* SSL * */ |
| jint state) { |
| SSL *ssl_ = J2P(ssl, SSL *); |
| |
| if (ssl_ == NULL) { |
| tcn_ThrowException(e, "ssl is null"); |
| return; |
| } |
| |
| UNREFERENCED(o); |
| |
| 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 *); |
| |
| if (ssl_ == NULL) { |
| tcn_ThrowException(e, "ssl is null"); |
| } else { |
| UNREFERENCED(o); |
| |
| 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_FREE_CSTRING(hostname); |
| } |
| |
| TCN_IMPLEMENT_CALL(void, SSL, setHostNameValidation)(TCN_STDARGS, jlong sslAddress, jint flags, jstring hostnameString) { |
| SSL* ssl = J2P(sslAddress, SSL*); |
| |
| 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(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_ASSERT(ssl_ != NULL); |
| |
| UNREFERENCED(o); |
| |
| ciphers = SSL_get_ciphers(ssl_); |
| len = sk_SSL_CIPHER_num(ciphers); |
| |
| array = (*e)->NewObjectArray(e, len, tcn_get_string_class(), 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(void, SSL, setCertificateBio)(TCN_STDARGS, jlong ssl, |
| jlong cert, jlong key, |
| jstring password) |
| { |
| 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]; |
| |
| UNREFERENCED(o); |
| TCN_ASSERT(ssl != NULL); |
| |
| if (key <= 0) { |
| key = cert; |
| } |
| |
| if (cert <= 0 || key <= 0) { |
| tcn_Throw(e, "No Certificate file specified or invalid file format"); |
| goto cleanup; |
| } |
| |
| 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; |
| } |
| |
| 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_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(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]; |
| |
| UNREFERENCED(o); |
| TCN_ASSERT(ssl_ != NULL); |
| TCN_ASSERT(b != NULL); |
| |
| 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(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]; |
| |
| UNREFERENCED(o); |
| |
| if (bio == NULL) { |
| tcn_Throw(e, "Unable to load certificate key"); |
| goto cleanup; |
| } |
| |
| 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; |
| } |
| |
| cleanup: |
| TCN_FREE_CSTRING(password); |
| return P2J(pkey); |
| } |
| |
| TCN_IMPLEMENT_CALL(void, SSL, freePrivateKey)(TCN_STDARGS, jlong privateKey) |
| { |
| EVP_PKEY *key = J2P(privateKey, EVP_PKEY *); |
| UNREFERENCED(o); |
| EVP_PKEY_free(key); // Safe to call with NULL as well. |
| } |
| |
| TCN_IMPLEMENT_CALL(long, SSL, parseX509Chain)(TCN_STDARGS, jlong x509ChainBio) |
| { |
| BIO *cert_bio = J2P(x509ChainBio, BIO *); |
| X509* cert = NULL; |
| STACK_OF(X509) *chain = NULL; |
| char err[ERR_LEN]; |
| unsigned long error; |
| int n = 0; |
| |
| UNREFERENCED(o); |
| |
| if (cert_bio == NULL) { |
| tcn_Throw(e, "No Certificate specified or invalid format"); |
| goto cleanup; |
| } |
| |
| 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++; |
| } |
| |
| // 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)) { |
| |
| ERR_error_string_n(ERR_get_error(), err, ERR_LEN); |
| tcn_Throw(e, "Invalid format (%s)", err); |
| goto cleanup; |
| } |
| ERR_clear_error(); |
| } |
| |
| return P2J(chain); |
| |
| cleanup: |
| ERR_clear_error(); |
| sk_X509_pop_free(chain, X509_free); |
| X509_free(cert); |
| return 0; |
| } |
| |
| TCN_IMPLEMENT_CALL(void, SSL, freeX509Chain)(TCN_STDARGS, jlong x509Chain) |
| { |
| STACK_OF(X509) *chain = J2P(x509Chain, STACK_OF(X509) *); |
| UNREFERENCED(o); |
| sk_X509_pop_free(chain, X509_free); |
| } |
| |