| /* 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 "tcn.h" | 
 | #include "apr_file_io.h" | 
 | #include "apr_thread_mutex.h" | 
 | #include "apr_atomic.h" | 
 | #include "apr_poll.h" | 
 |  | 
 | #include "ssl_private.h" | 
 |  | 
 | static int ssl_initialized = 0; | 
 | static char *ssl_global_rand_file = NULL; | 
 | extern apr_pool_t *tcn_global_pool; | 
 |  | 
 | ENGINE *tcn_ssl_engine = NULL; | 
 | tcn_pass_cb_t tcn_password_callback; | 
 |  | 
 | static BIO *key_log_file = NULL; | 
 |  | 
 | static void ssl_keylog_callback(const SSL *ssl, const char *line) | 
 | { | 
 |     if (key_log_file && line && *line) { | 
 |         BIO_puts(key_log_file, line); | 
 |         BIO_puts(key_log_file, "\n"); | 
 |     } | 
 | } | 
 |  | 
 | /* From netty-tcnative */ | 
 | static jclass byteArrayClass; | 
 | static jclass stringClass; | 
 |  | 
 | /* | 
 |  * Grab well-defined DH parameters from OpenSSL, see the BN_get_rfc* | 
 |  * functions in <openssl/bn.h> for all available primes. | 
 |  */ | 
 | static DH *make_dh_params(BIGNUM *(*prime)(BIGNUM *)) | 
 | { | 
 |     DH *dh = DH_new(); | 
 |     BIGNUM *p, *g; | 
 |  | 
 |     if (!dh) { | 
 |         return NULL; | 
 |     } | 
 |     p = prime(NULL); | 
 |     g = BN_new(); | 
 |     if (g != NULL) { | 
 |         BN_set_word(g, 2); | 
 |     } | 
 |     if (!p || !g || !DH_set0_pqg(dh, p, NULL, g)) { | 
 |         DH_free(dh); | 
 |         BN_free(p); | 
 |         BN_free(g); | 
 |         return NULL; | 
 |     } | 
 |     return dh; | 
 | } | 
 |  | 
 | /* Storage and initialization for DH parameters. */ | 
 | static struct dhparam { | 
 |     BIGNUM *(*const prime)(BIGNUM *); /* function to generate... */ | 
 |     DH *dh;                           /* ...this, used for keys.... */ | 
 |     const unsigned int min;           /* ...of length >= this. */ | 
 | } dhparams[] = { | 
 |     { BN_get_rfc3526_prime_8192, NULL, 6145 }, | 
 |     { BN_get_rfc3526_prime_6144, NULL, 4097 }, | 
 |     { BN_get_rfc3526_prime_4096, NULL, 3073 }, | 
 |     { BN_get_rfc3526_prime_3072, NULL, 2049 }, | 
 |     { BN_get_rfc3526_prime_2048, NULL, 1025 }, | 
 |     { BN_get_rfc2409_prime_1024, NULL, 0 } | 
 | }; | 
 |  | 
 | static void init_dh_params(void) | 
 | { | 
 |     unsigned n; | 
 |  | 
 |     for (n = 0; n < sizeof(dhparams)/sizeof(dhparams[0]); n++) | 
 |         dhparams[n].dh = make_dh_params(dhparams[n].prime); | 
 | } | 
 |  | 
 | static void free_dh_params(void) | 
 | { | 
 |     unsigned n; | 
 |  | 
 |     /* DH_free() is a noop for a NULL parameter, so these are harmless | 
 |      * in the (unexpected) case where these variables are already | 
 |      * NULL. */ | 
 |     for (n = 0; n < sizeof(dhparams)/sizeof(dhparams[0]); n++) { | 
 |         DH_free(dhparams[n].dh); | 
 |         dhparams[n].dh = NULL; | 
 |     } | 
 | } | 
 |  | 
 | void SSL_callback_add_keylog(SSL_CTX *ctx) | 
 | { | 
 |     if (key_log_file) { | 
 |         SSL_CTX_set_keylog_callback(ctx, ssl_keylog_callback); | 
 |     } | 
 | } | 
 |  | 
 | /* Hand out the same DH structure though once generated as we leak | 
 |  * memory otherwise and freeing the structure up after use would be | 
 |  * hard to track and in fact is not needed at all as it is safe to | 
 |  * use the same parameters over and over again security wise (in | 
 |  * contrast to the keys itself) and code safe as the returned structure | 
 |  * is duplicated by OpenSSL anyway. Hence no modification happens | 
 |  * to our copy. */ | 
 | DH *SSL_get_dh_params(unsigned keylen) | 
 | { | 
 |     unsigned n; | 
 |  | 
 |     for (n = 0; n < sizeof(dhparams)/sizeof(dhparams[0]); n++) | 
 |         if (keylen >= dhparams[n].min) | 
 |             return dhparams[n].dh; | 
 |  | 
 |     return NULL; /* impossible to reach. */ | 
 | } | 
 |  | 
 | static void init_bio_methods(void); | 
 | static void free_bio_methods(void); | 
 |  | 
 | 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; | 
 |  | 
 |     free_bio_methods(); | 
 |     free_dh_params(); | 
 |  | 
 | #ifndef OPENSSL_NO_ENGINE | 
 |     if (tcn_ssl_engine != NULL) { | 
 |         /* Release the SSL Engine structural reference */ | 
 |         ENGINE_free(tcn_ssl_engine); | 
 |         tcn_ssl_engine = NULL; | 
 |     } | 
 | #endif | 
 |  | 
 |     /* Openssl v1.1+ handles all termination automatically. */ | 
 |  | 
 |     if (key_log_file) { | 
 |         BIO_free(key_log_file); | 
 |         key_log_file = NULL; | 
 |     } | 
 |  | 
 |     /* 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 LibreSSL | 
 |  */ | 
 |  | 
 | static unsigned long ssl_thread_id(void) | 
 | { | 
 |     return (unsigned long)tcn_get_thread_id(); | 
 | } | 
 |  | 
 | 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) | 
 | { | 
 |     char buffer[APR_PATH_MAX]; | 
 |     int n; | 
 |  | 
 |     if (file == NULL) | 
 |         file = ssl_global_rand_file; | 
 |     if (file && (strcmp(file, "builtin") == 0)) | 
 |         return -1; | 
 |     if (file == NULL) | 
 |         file = RAND_file_name(buffer, sizeof(buffer)); | 
 |     if (file) { | 
 |         if (strncmp(file, "egd:", 4) == 0) { | 
 | #ifndef OPENSSL_NO_EGD | 
 |             if ((n = RAND_egd(file + 4)) > 0) | 
 |                 return n; | 
 |             else | 
 | #endif | 
 |                 return -1; | 
 |         } | 
 |         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) | 
 | { | 
 |     char buffer[APR_PATH_MAX]; | 
 | #ifndef OPENSSL_NO_EGD | 
 |     int n; | 
 | #endif | 
 |  | 
 |     if (file == NULL) | 
 |         file = RAND_file_name(buffer, sizeof(buffer)); | 
 | #ifndef OPENSSL_NO_EGD | 
 |     else if ((n = RAND_egd(file)) > 0) { | 
 |         return 0; | 
 |     } | 
 | #endif | 
 |     if (file == NULL || !RAND_write_file(file)) | 
 |         return 0; | 
 |     else | 
 |         return 1; | 
 | } | 
 |  | 
 | 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(); | 
 | } | 
 |  | 
 | static int ssl_rand_make(const char *file, int len, int base64) | 
 | { | 
 |     int r; | 
 |     int num = len; | 
 |     BIO *out = NULL; | 
 |  | 
 |     out = BIO_new(BIO_s_file()); | 
 |     if (out == NULL) | 
 |         return 0; | 
 |     if ((r = BIO_write_filename(out, (char *)file)) < 0) { | 
 |         BIO_free_all(out); | 
 |         return 0; | 
 |     } | 
 |     if (base64) { | 
 |         BIO *b64 = BIO_new(BIO_f_base64()); | 
 |         if (b64 == NULL) { | 
 |             BIO_free_all(out); | 
 |             return 0; | 
 |         } | 
 |         out = BIO_push(b64, out); | 
 |     } | 
 |     while (num > 0) { | 
 |         unsigned char buf[4096]; | 
 |         int len = num; | 
 |         if (len > sizeof(buf)) | 
 |             len = sizeof(buf); | 
 |         r = RAND_bytes(buf, len); | 
 |         if (r <= 0) { | 
 |             BIO_free_all(out); | 
 |             return 0; | 
 |         } | 
 |         BIO_write(out, buf, len); | 
 |         num -= len; | 
 |     } | 
 |     r = BIO_flush(out); | 
 |     BIO_free_all(out); | 
 |     return r > 0 ? 1 : 0; | 
 | } | 
 |  | 
 | TCN_IMPLEMENT_CALL(jint, SSL, initialize)(TCN_STDARGS, jstring engine) | 
 | { | 
 |     jclass clazz; | 
 |     jclass sClazz; | 
 | #if !defined(OPENSSL_NO_ENGINE) | 
 |     apr_status_t err = APR_SUCCESS; | 
 | #endif | 
 |  | 
 |     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; | 
 |     } | 
 |     /* Openssl v1.1+ handles all initialisation automatically, apart | 
 |      * from hints as to how we want to use the library. | 
 |      * | 
 |      * We tell openssl we want to include engine support. | 
 |      */ | 
 |     OPENSSL_init_ssl(OPENSSL_INIT_ENGINE_ALL_BUILTIN, NULL); | 
 |  | 
 | #ifndef OPENSSL_NO_ENGINE | 
 |     if (J2S(engine)) { | 
 |         ENGINE *ee = NULL; | 
 |         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; | 
 |             } | 
 |         } | 
 |         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(), SSL_get_app_data3() and SSL_get_app_data4() at request time */ | 
 |     SSL_init_app_data_idx(); | 
 |  | 
 |     init_dh_params(); | 
 |     init_bio_methods(); | 
 |  | 
 |     /* | 
 |      * 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); | 
 |  | 
 |     if (!key_log_file) { | 
 |         char *key_log_file_name = getenv("SSLKEYLOGFILE"); | 
 |         if (key_log_file_name) { | 
 |             FILE *file = fopen(key_log_file_name, "a"); | 
 |             if (file) { | 
 |                 if (setvbuf(file, NULL, _IONBF, 0)) { | 
 |                     fclose(file); | 
 |                 } else { | 
 |                     key_log_file = BIO_new_fp(file, BIO_CLOSE); | 
 |                 } | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 |     return (jint)APR_SUCCESS; | 
 | } | 
 |  | 
 | 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) | 
 | { | 
 | #if defined(LIBRESSL_VERSION_NUMBER) | 
 |     UNREFERENCED(o); | 
 |     /* LibreSSL doesn't support FIPS */ | 
 |     return 0; | 
 | #else | 
 |     EVP_MD              *md; | 
 |     const OSSL_PROVIDER *provider; | 
 |     const char          *name; | 
 |     UNREFERENCED(o); | 
 |  | 
 |     // Maps the OpenSSL 3. x onwards behaviour to theOpenSSL 1.x API | 
 |  | 
 |     // Checks that FIPS is the default provider | 
 |     md = EVP_MD_fetch(NULL, "SHA-512", NULL); | 
 |     provider = EVP_MD_get0_provider(md); | 
 |     name = OSSL_PROVIDER_get0_name(provider); | 
 |     // Clean up | 
 |     EVP_MD_free(md); | 
 |  | 
 |     if (strcmp("fips", name)) { | 
 |         return 0; | 
 |     } else { | 
 |         return 1; | 
 |     } | 
 | #endif | 
 | } | 
 |  | 
 | TCN_IMPLEMENT_CALL(jint, SSL, fipsModeSet)(TCN_STDARGS, jint mode) | 
 | { | 
 |     int r = 0; | 
 |     UNREFERENCED(o); | 
 |  | 
 |     /* This method should never be called when using Tomcat Native 2.x onwards */ | 
 |     tcn_ThrowException(e, "fipsModeSet is not supported in Tomcat Native 2.x onwards."); | 
 |  | 
 |     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) | 
 | { | 
 |     BIO_JAVA *j; | 
 |     if (bi == NULL) | 
 |         return; | 
 |     j = (BIO_JAVA *)BIO_get_data(bi); | 
 |     if (j != NULL && BIO_test_flags(bi, SSL_BIO_FLAG_CALLBACK)) { | 
 |         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) | 
 | { | 
 |     BIO_JAVA *j; | 
 |     if (bi == NULL) | 
 |         return; | 
 |     j = (BIO_JAVA *)BIO_get_data(bi); | 
 |     if (j != NULL && BIO_test_flags(bi, SSL_BIO_FLAG_CALLBACK)) { | 
 |         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; | 
 |     BIO_set_shutdown(bi, 1); | 
 |     BIO_set_init(bi, 0); | 
 |     BIO_set_data(bi, (void *)j); | 
 |  | 
 |     return 1; | 
 | } | 
 |  | 
 | static int jbs_free(BIO *bi) | 
 | { | 
 |     BIO_JAVA *j; | 
 |     if (bi == NULL) | 
 |         return 0; | 
 |     j = (BIO_JAVA *)BIO_get_data(bi); | 
 |     if (j != NULL) { | 
 |         if (BIO_get_init(bi)) { | 
 |             JNIEnv   *e = NULL; | 
 |             BIO_set_init(bi, 0); | 
 |             tcn_get_java_env(&e); | 
 |             TCN_UNLOAD_CLASS(e, j->cb.obj); | 
 |         } | 
 |         OPENSSL_free(j); | 
 |     } | 
 |     BIO_set_data(bi, NULL); | 
 |     return 1; | 
 | } | 
 |  | 
 | static int jbs_write(BIO *b, const char *in, int inl) | 
 | { | 
 |     jint ret = -1; | 
 |     if (BIO_get_init(b) && in != NULL) { | 
 |         BIO_JAVA *j = (BIO_JAVA *)BIO_get_data(b); | 
 |         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)->ReleaseByteArrayElements(e, jb, (jbyte *)in, JNI_ABORT); | 
 |             (*e)->DeleteLocalRef(e, jb); | 
 |         } | 
 |     } | 
 |     /* From netty-tc-native, in the AF we were returning 0 */ | 
 |     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; | 
 |     if (BIO_get_init(b) && out != NULL) { | 
 |         BIO_JAVA *j = (BIO_JAVA *)BIO_get_data(b); | 
 |         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) { | 
 |                 jbyte *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; | 
 |     if (BIO_get_init(b) && in != NULL) { | 
 |         BIO_JAVA *j = (BIO_JAVA *)BIO_get_data(b); | 
 |         JNIEnv   *e = NULL; | 
 |         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; | 
 |     if (BIO_get_init(b) && out != NULL) { | 
 |         BIO_JAVA *j = (BIO_JAVA *)BIO_get_data(b); | 
 |         JNIEnv   *e = NULL; | 
 |         jobject  o; | 
 |         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)) { | 
 |                 int 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 = NULL; | 
 |  | 
 | static void init_bio_methods(void) | 
 | { | 
 |     jbs_methods = BIO_meth_new(BIO_TYPE_FILE, "Java Callback"); | 
 |     BIO_meth_set_write(jbs_methods, &jbs_write); | 
 |     BIO_meth_set_read(jbs_methods, &jbs_read); | 
 |     BIO_meth_set_puts(jbs_methods, &jbs_puts); | 
 |     BIO_meth_set_gets(jbs_methods, &jbs_gets); | 
 |     BIO_meth_set_ctrl(jbs_methods, &jbs_ctrl); | 
 |     BIO_meth_set_create(jbs_methods, &jbs_new); | 
 |     BIO_meth_set_destroy(jbs_methods, &jbs_free); | 
 | } | 
 |  | 
 | static void free_bio_methods(void) | 
 | { | 
 |     BIO_meth_free(jbs_methods); | 
 | } | 
 |  | 
 | static BIO_METHOD *BIO_jbs() | 
 | { | 
 |     return jbs_methods; | 
 | } | 
 |  | 
 | /*** Begin Twitter 1:1 API addition ***/ | 
 | TCN_IMPLEMENT_CALL(jint, SSL, getLastErrorNumber)(TCN_STDARGS) { | 
 |     UNREFERENCED_STDARGS; | 
 |     return SSL_ERR_get(); | 
 | } | 
 |  | 
 | static void ssl_info_callback(const SSL *ssl, int where, int ret) { | 
 |     int *handshakeCount = NULL; | 
 |     if (0 != (where & SSL_CB_HANDSHAKE_DONE)) { | 
 |         handshakeCount = (int*) SSL_get_app_data3(ssl); | 
 |         if (handshakeCount != NULL) { | 
 |             ++(*handshakeCount); | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | static apr_status_t ssl_con_pool_cleanup(void *data) | 
 | { | 
 |     SSL *ssl = (SSL*) data; | 
 |     int *destroyCount; | 
 |  | 
 |     TCN_ASSERT(ssl != 0); | 
 |  | 
 |     destroyCount = SSL_get_app_data4(ssl); | 
 |     if (destroyCount != NULL) { | 
 |         ++(*destroyCount); | 
 |     } | 
 |  | 
 |     return APR_SUCCESS; | 
 | } | 
 |  | 
 | TCN_IMPLEMENT_CALL(jlong /* SSL * */, SSL, newSSL)(TCN_STDARGS, | 
 |                                                    jlong ctx /* tcn_ssl_ctxt_t * */, | 
 |                                                    jboolean server) { | 
 |     tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); | 
 |     int *handshakeCount = malloc(sizeof(int)); | 
 |     int *destroyCount = malloc(sizeof(int)); | 
 |     SSL *ssl; | 
 |     apr_pool_t *p = NULL; | 
 |     tcn_ssl_conn_t *con; | 
 |  | 
 |     UNREFERENCED_STDARGS; | 
 |  | 
 |     TCN_ASSERT(ctx != 0); | 
 |  | 
 |     ssl = SSL_new(c->ctx); | 
 |     if (ssl == NULL) { | 
 |         free(handshakeCount); | 
 |         free(destroyCount); | 
 |         tcn_ThrowException(e, "cannot create new ssl"); | 
 |         return 0; | 
 |     } | 
 |  | 
 |     apr_pool_create(&p, c->pool); | 
 |     if (p == NULL) { | 
 |         free(handshakeCount); | 
 |         free(destroyCount); | 
 |         SSL_free(ssl); | 
 |         tcn_ThrowAPRException(e, apr_get_os_error()); | 
 |         return 0; | 
 |     } | 
 |  | 
 |     if ((con = apr_pcalloc(p, sizeof(tcn_ssl_conn_t))) == NULL) { | 
 |         free(handshakeCount); | 
 |         free(destroyCount); | 
 |         SSL_free(ssl); | 
 |         apr_pool_destroy(p); | 
 |         tcn_ThrowAPRException(e, apr_get_os_error()); | 
 |         return 0; | 
 |     } | 
 |     con->pool = p; | 
 |     con->ctx  = c; | 
 |     con->ssl  = ssl; | 
 |     con->shutdown_type = c->shutdown_type; | 
 |  | 
 |     /* Store the handshakeCount in the SSL instance. */ | 
 |     *handshakeCount = 0; | 
 |     SSL_set_app_data3(ssl, handshakeCount); | 
 |  | 
 |     /* Store the destroyCount in the SSL instance. */ | 
 |     *destroyCount = 0; | 
 |     SSL_set_app_data4(ssl, destroyCount); | 
 |  | 
 |     /* 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); | 
 |     SSL_set_app_data(ssl, con); | 
 |     /* Register cleanup that prevent double destruction */ | 
 |     apr_pool_cleanup_register(con->pool, (const void *)ssl, | 
 |                               ssl_con_pool_cleanup, | 
 |                               apr_pool_cleanup_null); | 
 |  | 
 |     return P2J(ssl); | 
 | } | 
 |  | 
 | /* How much did SSL write into this BIO? */ | 
 | TCN_IMPLEMENT_CALL(jint /* nbytes */, SSL, pendingWrittenBytesInBIO)(TCN_STDARGS, | 
 |                                                                      jlong bio /* BIO * */) { | 
 |     UNREFERENCED_STDARGS; | 
 |  | 
 |     return BIO_ctrl_pending(J2P(bio, BIO *)); | 
 | } | 
 |  | 
 | /* How much is available for reading in the given SSL struct? */ | 
 | TCN_IMPLEMENT_CALL(jint, SSL, pendingReadableBytesInSSL)(TCN_STDARGS, jlong ssl /* SSL * */) { | 
 |     UNREFERENCED_STDARGS; | 
 |  | 
 |     return SSL_pending(J2P(ssl, 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) */) { | 
 |     UNREFERENCED_STDARGS; | 
 |  | 
 |     return BIO_write(J2P(bio, BIO *), J2P(wbuf, void *), 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 */) { | 
 |     UNREFERENCED_STDARGS; | 
 |  | 
 |     return BIO_read(J2P(bio, BIO *), J2P(rbuf, void *), 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) */) { | 
 |     UNREFERENCED_STDARGS; | 
 |  | 
 |     return SSL_write(J2P(ssl, SSL *), J2P(wbuf, void *), 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 */) { | 
 |     UNREFERENCED_STDARGS; | 
 |  | 
 |     return SSL_read(J2P(ssl, SSL *), J2P(rbuf, void *), rlen); | 
 | } | 
 |  | 
 | /* Get the shutdown status of the engine */ | 
 | TCN_IMPLEMENT_CALL(jint /* status */, SSL, getShutdown)(TCN_STDARGS, | 
 |                                                         jlong ssl /* SSL * */) { | 
 |     UNREFERENCED_STDARGS; | 
 |  | 
 |     return SSL_get_shutdown(J2P(ssl, SSL *)); | 
 | } | 
 |  | 
 | /* Free the SSL * and its associated internal BIO */ | 
 | TCN_IMPLEMENT_CALL(void, SSL, freeSSL)(TCN_STDARGS, | 
 |                                        jlong ssl /* SSL * */) { | 
 |     SSL *ssl_ = J2P(ssl, SSL *); | 
 |     int *handshakeCount = SSL_get_app_data3(ssl_); | 
 |     int *destroyCount = SSL_get_app_data4(ssl_); | 
 |     tcn_ssl_conn_t *con = SSL_get_app_data(ssl_); | 
 |  | 
 |     UNREFERENCED_STDARGS; | 
 |  | 
 |     if (destroyCount != NULL) { | 
 |         if (*destroyCount == 0) { | 
 |             apr_pool_destroy(con->pool); | 
 |         } | 
 |         free(destroyCount); | 
 |     } | 
 |     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; | 
 |  | 
 |     UNREFERENCED(o); | 
 |  | 
 |     if (ssl_ == NULL) { | 
 |         tcn_ThrowException(e, "ssl is null"); | 
 |         goto fail; | 
 |     } | 
 |  | 
 |     if (BIO_new_bio_pair(&internal_bio, 0, &network_bio, 0) != 1) { | 
 |         tcn_ThrowException(e, "BIO_new_bio_pair failed"); | 
 |         goto fail; | 
 |     } | 
 |  | 
 |     SSL_set_bio(ssl_, internal_bio, internal_bio); | 
 |  | 
 |     return P2J(network_bio); | 
 |  fail: | 
 |     return 0; | 
 | } | 
 |  | 
 | /* Free a BIO * (typically, the network BIO) */ | 
 | TCN_IMPLEMENT_CALL(void, SSL, freeBIO)(TCN_STDARGS, | 
 |                                        jlong bio /* BIO * */) { | 
 |     BIO *bio_; | 
 |     UNREFERENCED_STDARGS; | 
 |  | 
 |     bio_ = J2P(bio, BIO *); | 
 |     BIO_free(bio_); | 
 | } | 
 |  | 
 | /* Send CLOSE_NOTIFY to peer */ | 
 | TCN_IMPLEMENT_CALL(jint /* status */, SSL, shutdownSSL)(TCN_STDARGS, | 
 |                                                         jlong ssl /* SSL * */) { | 
 |     UNREFERENCED_STDARGS; | 
 |  | 
 |     return SSL_shutdown(J2P(ssl, SSL *)); | 
 | } | 
 |  | 
 | /* Read which cipher was negotiated for the given SSL *. */ | 
 | TCN_IMPLEMENT_CALL(jstring, SSL, getCipherForSSL)(TCN_STDARGS, | 
 |                                                   jlong ssl /* SSL * */) | 
 | { | 
 |     UNREFERENCED_STDARGS; | 
 |  | 
 |     return AJP_TO_JSTRING(SSL_get_cipher(J2P(ssl, SSL*))); | 
 | } | 
 |  | 
 | /* Read which protocol was negotiated for the given SSL *. */ | 
 | TCN_IMPLEMENT_CALL(jstring, SSL, getVersion)(TCN_STDARGS, | 
 |                                                   jlong ssl /* SSL * */) | 
 | { | 
 |     UNREFERENCED_STDARGS; | 
 |  | 
 |     return AJP_TO_JSTRING(SSL_get_version(J2P(ssl, SSL*))); | 
 | } | 
 |  | 
 | /* Is the handshake over yet? */ | 
 | TCN_IMPLEMENT_CALL(jint, SSL, isInInit)(TCN_STDARGS, | 
 |                                         jlong ssl /* SSL * */) { | 
 |     SSL *ssl_ = J2P(ssl, SSL *); | 
 |  | 
 |     UNREFERENCED(o); | 
 |  | 
 |     if (ssl_ == NULL) { | 
 |         tcn_ThrowException(e, "ssl is null"); | 
 |         return 0; | 
 |     } else { | 
 |         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_); | 
 | } | 
 |  | 
 | 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(jint, SSL, renegotiatePending)(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_pending(ssl_); | 
 | } | 
 |  | 
 | TCN_IMPLEMENT_CALL(jint, SSL, verifyClientPostHandshake)(TCN_STDARGS, | 
 |                                                          jlong ssl /* SSL * */) { | 
 | #if defined(SSL_OP_NO_TLSv1_3) | 
 |     SSL *ssl_ = J2P(ssl, SSL *); | 
 |     tcn_ssl_conn_t *con; | 
 |  | 
 |     if (ssl_ == NULL) { | 
 |         tcn_ThrowException(e, "ssl is null"); | 
 |         return 0; | 
 |     } | 
 |  | 
 |     UNREFERENCED(o); | 
 |  | 
 |     con = (tcn_ssl_conn_t *)SSL_get_app_data(ssl_); | 
 |     con->pha_state = PHA_STARTED; | 
 |  | 
 |     return SSL_verify_client_post_handshake(ssl_); | 
 | #else | 
 |     return 0; | 
 | #endif | 
 | } | 
 |  | 
 | TCN_IMPLEMENT_CALL(jint, SSL, getPostHandshakeAuthInProgress)(TCN_STDARGS, | 
 |                                                               jlong ssl /* SSL * */) { | 
 | #if defined(SSL_OP_NO_TLSv1_3) | 
 |     SSL *ssl_ = J2P(ssl, SSL *); | 
 |     tcn_ssl_conn_t *con; | 
 |  | 
 |     if (ssl_ == NULL) { | 
 |         tcn_ThrowException(e, "ssl is null"); | 
 |         return 0; | 
 |     } | 
 |  | 
 |     UNREFERENCED(o); | 
 |  | 
 |     con = (tcn_ssl_conn_t *)SSL_get_app_data(ssl_); | 
 |  | 
 |     return (con->pha_state == PHA_STARTED); | 
 | #else | 
 |     return 0; | 
 | #endif | 
 | } | 
 |  | 
 | /*** End Twitter API Additions ***/ | 
 |  | 
 | /*** Apple API Additions ***/ | 
 |  | 
 | TCN_IMPLEMENT_CALL(jstring, SSL, getAlpnSelected)(TCN_STDARGS, | 
 |                                                          jlong ssl /* SSL * */) { | 
 |     /* Looks fishy we have the same in sslnetwork.c, it set by socket/connection */ | 
 |     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, (const char *) proto, (size_t) proto_len); | 
 | } | 
 |  | 
 | 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_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 = (X509*) sk_X509_value(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) | 
 | { | 
 |     const SSL *ssl_ = J2P(ssl, SSL *); | 
 |     const SSL_SESSION *session; | 
 |  | 
 |     if (ssl_ == NULL) { | 
 |         tcn_ThrowException(e, "ssl is null"); | 
 |         return 0; | 
 |     } | 
 |  | 
 |     UNREFERENCED(o); | 
 |  | 
 |     session  = SSL_get_session(ssl_); | 
 |     if (session) { | 
 |         return SSL_get_time(session); | 
 |     } else { | 
 |         tcn_ThrowException(e, "ssl session is null"); | 
 |         return 0; | 
 |     } | 
 | } | 
 |  | 
 | 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); | 
 |  | 
 |     if (c == NULL) { | 
 |         tcn_ThrowException(e, "context is null"); | 
 |         return; | 
 |     } | 
 |     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 *); | 
 |  | 
 |     UNREFERENCED_STDARGS; | 
 |  | 
 |     if (ssl_ == NULL) { | 
 |         tcn_ThrowException(e, "ssl is null"); | 
 |         return; | 
 |     } | 
 |  | 
 | #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 *); | 
 |  | 
 |     UNREFERENCED_STDARGS; | 
 |  | 
 |     if (ssl_ == NULL) { | 
 |         tcn_ThrowException(e, "ssl is null"); | 
 |         return 0; | 
 |     } | 
 |  | 
 |     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 *); | 
 |  | 
 |     UNREFERENCED_STDARGS; | 
 |  | 
 |     if (ssl_ == NULL) { | 
 |         tcn_ThrowException(e, "ssl is null"); | 
 |         return NULL; | 
 |     } | 
 |  | 
 |     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, stringClass, NULL); | 
 |  | 
 |     for (i = 0; i < len; i++) { | 
 |         cipher = (SSL_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; | 
 |     SSL *ssl_ = J2P(ssl, SSL *); | 
 |     TCN_ALLOC_CSTRING(ciphers); | 
 |  | 
 |     UNREFERENCED_STDARGS; | 
 |  | 
 |     if (ssl_ == NULL) { | 
 |         TCN_FREE_CSTRING(ciphers); | 
 |         tcn_ThrowException(e, "ssl is null"); | 
 |         return JNI_FALSE; | 
 |     } | 
 |  | 
 |     UNREFERENCED(o); | 
 |     if (!J2S(ciphers)) { | 
 |         TCN_FREE_CSTRING(ciphers); | 
 |         return JNI_FALSE; | 
 |     } | 
 |     if (!SSL_set_cipher_list(ssl_, J2S(ciphers))) { | 
 |         char err[256]; | 
 |         ERR_error_string(SSL_ERR_get(), 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; | 
 |     const 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 (NULL == session) { | 
 |         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; | 
 | } | 
 |  | 
 | /*** End Apple API Additions ***/ |