blob: f8302d43a46e733a518d1fde35133b5d39a3cc6e [file] [log] [blame]
/* 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.
*/
/*
*
* @author Mladen Turk
* @version $Id: ssl.c 1649733 2015-01-06 04:42:24Z billbarker $
*/
#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 "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;
const char* file;
int line;
apr_thread_mutex_t *mutex;
};
/*
* 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_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); \
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_EPHEMERAL_RSA
| SSL_OP_EPHEMERAL_RSA
#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 int ssl_tmp_key_init_rsa(int bits, int idx)
{
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
return 0;
#else
#ifdef OPENSSL_FIPS
/**
* Short RSA keys cannot be generated in FIPS mode.
*/
if (bits < 1024)
return 0;
#endif
BIGNUM *e = BN_new();
RSA *rsa = RSA_new();
int ret = 1;
if (e == NULL ||
rsa == NULL ||
!BN_set_word(e, RSA_F4) ||
RSA_generate_key_ex(rsa, bits, e, NULL) != 1) {
goto err;
}
SSL_temp_keys[idx] = rsa;
rsa = NULL;
ret = 0;
err:
BN_free(e);
RSA_free(rsa);
return ret;
#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
}
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)))
return 1;
else
return 0;
#else
return 0;
#endif
}
TCN_IMPLEMENT_CALL(jint, SSL, version)(TCN_STDARGS)
{
UNREFERENCED_STDARGS;
return (jint) SSLeay();
}
TCN_IMPLEMENT_CALL(jstring, SSL, versionString)(TCN_STDARGS)
{
UNREFERENCED(o);
return AJP_TO_JSTRING(SSLeay_version(SSLEAY_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;
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.
*/
#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
ERR_remove_state(0);
/* 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 apr_status_t ssl_thread_cleanup(void *data)
{
UNREFERENCED(data);
CRYPTO_set_locking_callback(NULL);
CRYPTO_set_id_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_set_id_callback(ssl_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);
}
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);
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 (SSLeay() < 0x0090700L) {
TCN_FREE_CSTRING(engine);
tcn_ThrowAPRException(e, APR_EINVAL);
ssl_initialized = 0;
return (jint)APR_EINVAL;
}
#ifndef OPENSSL_IS_BORINGSSL
/* We must register the library in full, to ensure our configuration
* code can successfully test the SSL environment.
*/
CRYPTO_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 && !defined(OPENSSL_IS_BORINGSSL)
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
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();
SSL_TMP_KEYS_INIT(r);
if (r) {
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);
// 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;
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 (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);
}
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];
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;
handshakeCount = malloc(sizeof(int));
ssl = SSL_new(c->ctx);
if (ssl == NULL) {
tcn_ThrowException(e, "cannot create new ssl");
return 0;
}
// Store the handshakeCount in the SSL instance.
*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);
}
// 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) {
SSL *ssl_ = J2P(ssl, SSL *);
if (ssl_ == NULL) {
tcn_ThrowException(e, "ssl is null");
return 0;
}
UNREFERENCED_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_ThrowException(e, "bio is null");
return 0;
}
if (w == NULL) {
tcn_ThrowException(e, "wbuf is null");
return 0;
}
UNREFERENCED_STDARGS;
return BIO_write(b, w, 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 *);
if (b == NULL) {
tcn_ThrowException(e, "bio is null");
return 0;
}
if (r == NULL) {
tcn_ThrowException(e, "rbuf is null");
return 0;
}
UNREFERENCED_STDARGS;
return BIO_read(b, r, rlen);
}
// 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;
SSL *ssl_ = J2P(ssl, SSL *);
if (ssl_ == NULL) {
tcn_ThrowException(e, "ssl is null");
return;
}
handshakeCount = SSL_get_app_data3(ssl_);
UNREFERENCED_STDARGS;
if (handshakeCount != NULL) {
free(handshakeCount);
}
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;
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");
return 0;
}
UNREFERENCED(o);
SSL_set_bio(ssl_, internal_bio, internal_bio);
return P2J(network_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;
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_num((_STACK*) 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 = (X509*) sk_value((_STACK*) sk, i);
buf = NULL;
length = i2d_X509(cert, &buf);
if (length < 0) {
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[256];
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_ctxt_t *c;
int verify;
SSL *ssl_ = J2P(ssl, SSL *);
if (ssl_ == NULL) {
tcn_ThrowException(e, "ssl is null");
return;
}
c = SSL_get_app_data2(ssl_);
verify = SSL_VERIFY_NONE;
UNREFERENCED(o);
TCN_ASSERT(c->ctx != 0);
c->verify_mode = level;
if (c->verify_mode == SSL_CVERIFY_UNSET)
c->verify_mode = SSL_CVERIFY_NONE;
if (depth > 0)
c->verify_depth = depth;
/*
* Configure callbacks for SSL context
*/
if (c->verify_mode == SSL_CVERIFY_REQUIRE)
verify |= SSL_VERIFY_PEER_STRICT;
if ((c->verify_mode == SSL_CVERIFY_OPTIONAL) ||
(c->verify_mode == SSL_CVERIFY_OPTIONAL_NO_CA))
verify |= SSL_VERIFY_PEER;
if (!c->store) {
if (SSL_CTX_set_default_verify_paths(c->ctx)) {
c->store = SSL_CTX_get_cert_store(c->ctx);
X509_STORE_set_flags(c->store, 0);
}
else {
/* XXX: See if this is fatal */
}
}
SSL_set_verify(ssl_, verify, SSL_callback_SSL_verify);
}
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;
#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(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;
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_num((_STACK*) 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, stringClass, NULL);
for (i = 0; i < len; i++) {
cipher = (SSL_CIPHER*) sk_value((_STACK*) 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[256];
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_);
if (handshakeCount != NULL) {
return *handshakeCount;
}
return 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);
}
/*** 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;
}
TCN_IMPLEMENT_CALL(jint, SSL, initialize)(TCN_STDARGS, jstring engine)
{
UNREFERENCED(o);
UNREFERENCED(engine);
tcn_ThrowAPRException(e, APR_ENOTIMPL);
return (jint)APR_ENOTIMPL;
}
TCN_IMPLEMENT_CALL(jboolean, SSL, randLoad)(TCN_STDARGS, jstring file)
{
UNREFERENCED_STDARGS;
UNREFERENCED(file);
return JNI_FALSE;
}
TCN_IMPLEMENT_CALL(jboolean, SSL, randSave)(TCN_STDARGS, jstring file)
{
UNREFERENCED_STDARGS;
return JNI_FALSE;
}
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;
}
TCN_IMPLEMENT_CALL(void, SSL, randSet)(TCN_STDARGS, jstring file)
{
UNREFERENCED_STDARGS;
UNREFERENCED(file);
}
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_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;
}
TCN_IMPLEMENT_CALL(jboolean, SSL, loadDSATempKey)(TCN_STDARGS, jint idx,
jstring file)
{
UNREFERENCED_STDARGS;
UNREFERENCED(idx);
UNREFERENCED(file);
return JNI_FALSE;
}
TCN_IMPLEMENT_CALL(jstring, SSL, getLastError)(TCN_STDARGS)
{
UNREFERENCED_STDARGS;
return NULL;
}
TCN_IMPLEMENT_CALL(jboolean, SSL, hasOp)(TCN_STDARGS, jint op)
{
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;
}
TCN_IMPLEMENT_CALL(jint, SSL, getShutdown)(TCN_STDARGS, jlong ssl) {
UNREFERENCED(o);
UNREFERENCED(ssl);
tcn_ThrowException(e, "Not implemented");
return 0;
}
TCN_IMPLEMENT_CALL(void, SSL, setShutdown)(TCN_STDARGS, jlong ssl, jint mode) {
UNREFERENCED(o);
UNREFERENCED(ssl);
UNREFERENCED(mode);
tcn_ThrowException(e, "Not implemented");
}
TCN_IMPLEMENT_CALL(void, SSL, freeSSL)(TCN_STDARGS, jlong ssl) {
UNREFERENCED(o);
UNREFERENCED(ssl);
tcn_ThrowException(e, "Not implemented");
}
TCN_IMPLEMENT_CALL(jlong, SSL, makeNetworkBIO)(TCN_STDARGS, jlong ssl) {
UNREFERENCED(o);
UNREFERENCED(ssl);
tcn_ThrowException(e, "Not implemented");
return 0;
}
TCN_IMPLEMENT_CALL(void, SSL, freeBIO)(TCN_STDARGS, jlong bio) {
UNREFERENCED(o);
UNREFERENCED(bio);
tcn_ThrowException(e, "Not implemented");
}
TCN_IMPLEMENT_CALL(jint, SSL, shutdownSSL)(TCN_STDARGS, jlong ssl) {
UNREFERENCED(o);
UNREFERENCED(ssl);
tcn_ThrowException(e, "Not implemented");
return 0;
}
TCN_IMPLEMENT_CALL(jstring, SSL, getCipherForSSL)(TCN_STDARGS, jlong ssl) {
UNREFERENCED(o);
UNREFERENCED(ssl);
tcn_ThrowException(e, "Not implemented");
return NULL;
}
TCN_IMPLEMENT_CALL(jint, SSL, isInInit)(TCN_STDARGS, jlong ssl) {
UNREFERENCED(o);
UNREFERENCED(ssl);
tcn_ThrowException(e, "Not implemented");
return 0;
}
TCN_IMPLEMENT_CALL(jint, SSL, doHandshake)(TCN_STDARGS, jlong ssl) {
UNREFERENCED(o);
UNREFERENCED(ssl);
tcn_ThrowException(e, "Not implemented");
}
TCN_IMPLEMENT_CALL(jstring, SSL, getNextProtoNegotiated)(TCN_STDARGS, jlong ssl) {
UNREFERENCED(o);
UNREFERENCED(ssl);
tcn_ThrowException(e, "Not implemented");
return NULL;
}
/*** End Twitter 1:1 API addition ***/
/*** Begin Apple 1:1 API addition ***/
TCN_IMPLEMENT_CALL(jstring, SSL, getAlpnSelected)(TCN_STDARGS, jlong ssl) {
UNREFERENCED(o);
UNREFERENCED(ssl);
tcn_ThrowException(e, "Not implemented");
return NULL;
}
TCN_IMPLEMENT_CALL(jobjectArray, SSL, getPeerCertChain)(TCN_STDARGS, jlong ssl)
{
UNREFERENCED(o);
UNREFERENCED(ssl);
tcn_ThrowException(e, "Not implemented");
return NULL;
}
TCN_IMPLEMENT_CALL(jbyteArray, SSL, getPeerCertificate)(TCN_STDARGS, jlong ssl)
{
UNREFERENCED(o);
UNREFERENCED(ssl);
tcn_ThrowException(e, "Not implemented");
return NULL;
}
TCN_IMPLEMENT_CALL(jstring, SSL, getErrorString)(TCN_STDARGS, jlong number)
{
UNREFERENCED(o);
tcn_ThrowException(e, "Not implemented");
return NULL;
}
TCN_IMPLEMENT_CALL(jstring, SSL, getVersion)(TCN_STDARGS, jlong ssl)
{
UNREFERENCED(o);
UNREFERENCED(ssl);
tcn_ThrowException(e, "Not implemented");
return NULL;
}
TCN_IMPLEMENT_CALL(jlong, SSL, getTime)(TCN_STDARGS, jlong ssl)
{
UNREFERENCED(o);
UNREFERENCED(ssl);
tcn_ThrowException(e, "Not implemented");
return 0;
}
TCN_IMPLEMENT_CALL(jlong, SSL, getTimeout)(TCN_STDARGS, jlong ssl)
{
UNREFERENCED(o);
UNREFERENCED(ssl);
tcn_ThrowException(e, "Not implemented");
return 0;
}
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;
}
TCN_IMPLEMENT_CALL(void, SSL, setVerify)(TCN_STDARGS, jlong ssl,
jint level, jint depth)
{
UNREFERENCED(o);
UNREFERENCED(ssl);
tcn_ThrowException(e, "Not implemented");
}
TCN_IMPLEMENT_CALL(void, SSL, setOptions)(TCN_STDARGS, jlong ssl,
jint opt)
{
UNREFERENCED_STDARGS;
UNREFERENCED(ssl);
UNREFERENCED(opt);
tcn_ThrowException(e, "Not implemented");
}
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");
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)
{
UNREFERENCED(o);
tcn_ThrowException(e, "Not implemented");
}
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