/* * Copyright 2016 The Netty Project * * The Netty Project licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ /* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "tcn.h" #include "apr_thread_rwlock.h" #include "apr_atomic.h" #include "ssl_private.h" #include extern apr_pool_t *tcn_global_pool; static apr_status_t ssl_context_cleanup(void *data) { tcn_ssl_ctxt_t *c = (tcn_ssl_ctxt_t *)data; JNIEnv *e; if (c) { SSL_CTX_free(c->ctx); // this function is safe to call with NULL c->ctx = NULL; if (c->verifier != NULL) { tcn_get_java_env(&e); (*e)->DeleteGlobalRef(e, c->verifier); c->verifier = NULL; } c->verifier_method = NULL; if (c->cert_requested_callback != NULL) { tcn_get_java_env(&e); (*e)->DeleteGlobalRef(e, c->cert_requested_callback); c->cert_requested_callback = NULL; } c->cert_requested_callback_method = NULL; if (c->next_proto_data != NULL) { free(c->next_proto_data); c->next_proto_data = NULL; } c->next_proto_len = 0; if (c->alpn_proto_data != NULL) { free(c->alpn_proto_data); c->alpn_proto_data = NULL; } c->alpn_proto_len = 0; apr_thread_rwlock_destroy(c->mutex); if (c->ticket_keys != NULL) { free(c->ticket_keys); c->ticket_keys = NULL; } c->ticket_keys_len = 0; if (c->password != NULL) { free(c->password); c->password = NULL; } } return APR_SUCCESS; } /* Initialize server context */ TCN_IMPLEMENT_CALL(jlong, SSLContext, make)(TCN_STDARGS, jint protocol, jint mode) { apr_pool_t *p = NULL; tcn_ssl_ctxt_t *c = NULL; SSL_CTX *ctx = NULL; UNREFERENCED(o); switch (protocol) { case SSL_PROTOCOL_TLS: case SSL_PROTOCOL_ALL: if (mode == SSL_MODE_CLIENT) ctx = SSL_CTX_new(SSLv23_client_method()); else if (mode == SSL_MODE_SERVER) ctx = SSL_CTX_new(SSLv23_server_method()); else ctx = SSL_CTX_new(SSLv23_method()); break; case SSL_PROTOCOL_TLSV1_2: #ifndef OPENSSL_NO_TLS1 if (mode == SSL_MODE_CLIENT) ctx = SSL_CTX_new(TLSv1_2_client_method()); else if (mode == SSL_MODE_SERVER) ctx = SSL_CTX_new(TLSv1_2_server_method()); else ctx = SSL_CTX_new(TLSv1_2_method()); #endif break; case SSL_PROTOCOL_TLSV1_1: #ifndef OPENSSL_NO_TLS1 if (mode == SSL_MODE_CLIENT) ctx = SSL_CTX_new(TLSv1_1_client_method()); else if (mode == SSL_MODE_SERVER) ctx = SSL_CTX_new(TLSv1_1_server_method()); else ctx = SSL_CTX_new(TLSv1_1_method()); #endif break; case SSL_PROTOCOL_TLSV1: #ifndef OPENSSL_NO_TLS1 if (mode == SSL_MODE_CLIENT) ctx = SSL_CTX_new(TLSv1_client_method()); else if (mode == SSL_MODE_SERVER) ctx = SSL_CTX_new(TLSv1_server_method()); else ctx = SSL_CTX_new(TLSv1_method()); #endif break; case SSL_PROTOCOL_SSLV3: #ifndef OPENSSL_NO_SSL3 if (mode == SSL_MODE_CLIENT) ctx = SSL_CTX_new(SSLv3_client_method()); else if (mode == SSL_MODE_SERVER) ctx = SSL_CTX_new(SSLv3_server_method()); else ctx = SSL_CTX_new(SSLv3_method()); #endif break; case SSL_PROTOCOL_SSLV2: #if (OPENSSL_VERSION_NUMBER < 0x10100000L) && !defined(OPENSSL_NO_SSL2) if (mode == SSL_MODE_CLIENT) ctx = SSL_CTX_new(SSLv2_client_method()); else if (mode == SSL_MODE_SERVER) ctx = SSL_CTX_new(SSLv2_server_method()); else ctx = SSL_CTX_new(SSLv2_method()); #endif break; default: // Try to give the user the highest supported protocol. #ifndef OPENSSL_NO_TLS1 if (protocol & SSL_PROTOCOL_TLSV1_2) { if (mode == SSL_MODE_CLIENT) ctx = SSL_CTX_new(TLSv1_2_client_method()); else if (mode == SSL_MODE_SERVER) ctx = SSL_CTX_new(TLSv1_2_server_method()); else ctx = SSL_CTX_new(TLSv1_2_method()); break; } else if (protocol & SSL_PROTOCOL_TLSV1_1) { if (mode == SSL_MODE_CLIENT) ctx = SSL_CTX_new(TLSv1_1_client_method()); else if (mode == SSL_MODE_SERVER) ctx = SSL_CTX_new(TLSv1_1_server_method()); else ctx = SSL_CTX_new(TLSv1_1_method()); break; } else if (protocol & SSL_PROTOCOL_TLSV1) { if (mode == SSL_MODE_CLIENT) ctx = SSL_CTX_new(TLSv1_client_method()); else if (mode == SSL_MODE_SERVER) ctx = SSL_CTX_new(TLSv1_server_method()); else ctx = SSL_CTX_new(TLSv1_method()); break; } #endif #ifndef OPENSSL_NO_SSL3 if (protocol & SSL_PROTOCOL_SSLV3) { if (mode == SSL_MODE_CLIENT) ctx = SSL_CTX_new(SSLv3_client_method()); else if (mode == SSL_MODE_SERVER) ctx = SSL_CTX_new(SSLv3_server_method()); else ctx = SSL_CTX_new(SSLv3_method()); break; } #endif #if (OPENSSL_VERSION_NUMBER < 0x10100000L) && !defined(OPENSSL_NO_SSL2) if (protocol & SSL_PROTOCOL_SSLV2) { if (mode == SSL_MODE_CLIENT) ctx = SSL_CTX_new(SSLv2_client_method()); else if (mode == SSL_MODE_SERVER) ctx = SSL_CTX_new(SSLv2_server_method()); else ctx = SSL_CTX_new(SSLv2_method()); break; } #endif tcn_Throw(e, "Unsupported SSL protocol (%d)", protocol); goto cleanup; } if (ctx == NULL) { char err[256]; ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Failed to initialize SSL_CTX (%s)", err); goto cleanup; } TCN_THROW_IF_ERR(apr_pool_create(&p, tcn_global_pool), p); if ((c = apr_pcalloc(p, sizeof(tcn_ssl_ctxt_t))) == NULL) { tcn_ThrowAPRException(e, apr_get_os_error()); goto cleanup; } c->protocol = protocol; c->mode = mode; c->ctx = ctx; c->pool = p; if (!(protocol & SSL_PROTOCOL_SSLV2)) SSL_CTX_set_options(c->ctx, SSL_OP_NO_SSLv2); if (!(protocol & SSL_PROTOCOL_SSLV3)) SSL_CTX_set_options(c->ctx, SSL_OP_NO_SSLv3); if (!(protocol & SSL_PROTOCOL_TLSV1)) SSL_CTX_set_options(c->ctx, SSL_OP_NO_TLSv1); #ifdef SSL_OP_NO_TLSv1_1 if (!(protocol & SSL_PROTOCOL_TLSV1_1)) SSL_CTX_set_options(c->ctx, SSL_OP_NO_TLSv1_1); #endif #ifdef SSL_OP_NO_TLSv1_2 if (!(protocol & SSL_PROTOCOL_TLSV1_2)) SSL_CTX_set_options(c->ctx, SSL_OP_NO_TLSv1_2); #endif /* * Configure additional context ingredients */ SSL_CTX_set_options(c->ctx, SSL_OP_SINGLE_DH_USE); #ifdef HAVE_ECC SSL_CTX_set_options(c->ctx, SSL_OP_SINGLE_ECDH_USE); #endif SSL_CTX_set_options(c->ctx, SSL_OP_NO_COMPRESSION); /* * Disallow a session from being resumed during a renegotiation, * so that an acceptable cipher suite can be negotiated. */ SSL_CTX_set_options(c->ctx, SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); /** * These options may be set by default but can be dangerous in practice [1]. * [1] https://www.openssl.org/docs/man1.0.1/ssl/SSL_CTX_set_options.html */ SSL_CTX_clear_options(c->ctx, SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION | SSL_OP_LEGACY_SERVER_CONNECT); /* Release idle buffers to the SSL_CTX free list */ SSL_CTX_set_mode(c->ctx, SSL_MODE_RELEASE_BUFFERS); /* Default session context id and cache size */ SSL_CTX_sess_set_cache_size(c->ctx, SSL_DEFAULT_CACHE_SIZE); /* Session cache is disabled by default */ SSL_CTX_set_session_cache_mode(c->ctx, SSL_SESS_CACHE_OFF); /* Longer session timeout */ SSL_CTX_set_timeout(c->ctx, 14400); EVP_Digest((const unsigned char *)SSL_DEFAULT_VHOST_NAME, (unsigned long)((sizeof SSL_DEFAULT_VHOST_NAME) - 1), &(c->context_id[0]), NULL, EVP_sha1(), NULL); if (mode) { #ifdef HAVE_ECC /* Set default (nistp256) elliptic curve for ephemeral ECDH keys */ EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); SSL_CTX_set_tmp_ecdh(c->ctx, ecdh); EC_KEY_free(ecdh); #endif SSL_CTX_set_tmp_dh_callback(c->ctx, SSL_callback_tmp_DH); } // Default depth is 100 and disabled according to https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_verify.html. c->verify_config.verify_depth = 100; c->verify_config.verify_mode = SSL_CVERIFY_NONE; /* Set default password callback */ SSL_CTX_set_default_passwd_cb(c->ctx, (pem_password_cb *)SSL_password_callback); SSL_CTX_set_default_passwd_cb_userdata(c->ctx, (void *) c->password); apr_thread_rwlock_create(&c->mutex, p); /* * Let us cleanup the ssl context when the pool is destroyed */ apr_pool_cleanup_register(p, (const void *)c, ssl_context_cleanup, apr_pool_cleanup_null); return P2J(c); cleanup: if (p != NULL) { apr_pool_destroy(p); } SSL_CTX_free(ctx); // this function is safe to call with NULL. return 0; } TCN_IMPLEMENT_CALL(jint, SSLContext, free)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); UNREFERENCED_STDARGS; TCN_ASSERT(ctx != 0); /* Run and destroy the cleanup callback */ int result = apr_pool_cleanup_run(c->pool, c, ssl_context_cleanup); apr_pool_destroy(c->pool); return result; } TCN_IMPLEMENT_CALL(void, SSLContext, setContextId)(TCN_STDARGS, jlong ctx, jstring id) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); TCN_ALLOC_CSTRING(id); TCN_ASSERT(ctx != 0); UNREFERENCED(o); if (J2S(id)) { EVP_Digest((const unsigned char *)J2S(id), (unsigned long)strlen(J2S(id)), &(c->context_id[0]), NULL, EVP_sha1(), NULL); } TCN_FREE_CSTRING(id); } TCN_IMPLEMENT_CALL(void, SSLContext, setOptions)(TCN_STDARGS, jlong ctx, jint opt) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); UNREFERENCED_STDARGS; TCN_ASSERT(ctx != 0); SSL_CTX_set_options(c->ctx, opt); } TCN_IMPLEMENT_CALL(jint, SSLContext, getOptions)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); UNREFERENCED_STDARGS; TCN_ASSERT(ctx != 0); return SSL_CTX_get_options(c->ctx); } TCN_IMPLEMENT_CALL(void, SSLContext, clearOptions)(TCN_STDARGS, jlong ctx, jint opt) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); UNREFERENCED_STDARGS; TCN_ASSERT(ctx != 0); SSL_CTX_clear_options(c->ctx, opt); } TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCipherSuite)(TCN_STDARGS, jlong ctx, jstring ciphers) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); TCN_ALLOC_CSTRING(ciphers); jboolean rv = JNI_TRUE; UNREFERENCED(o); TCN_ASSERT(ctx != 0); if (!J2S(ciphers)) return JNI_FALSE; if (!SSL_CTX_set_cipher_list(c->ctx, 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(jboolean, SSLContext, setCertificateChainFile)(TCN_STDARGS, jlong ctx, jstring file, jboolean skipfirst) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jboolean rv = JNI_FALSE; TCN_ALLOC_CSTRING(file); UNREFERENCED(o); TCN_ASSERT(ctx != 0); if (!J2S(file)) return JNI_FALSE; if (SSL_CTX_use_certificate_chain(c->ctx, J2S(file), skipfirst) > 0) rv = JNI_TRUE; TCN_FREE_CSTRING(file); return rv; } TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCertificateChainBio)(TCN_STDARGS, jlong ctx, jlong chain, jboolean skipfirst) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); BIO *b = J2P(chain, BIO *); UNREFERENCED(o); TCN_ASSERT(ctx != 0); if (b == NULL) return JNI_FALSE; if (SSL_CTX_use_certificate_chain_bio(c->ctx, b, skipfirst) > 0) { return JNI_TRUE; } return JNI_FALSE; } TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCACertificateBio)(TCN_STDARGS, jlong ctx, jlong certs) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); BIO *b = J2P(certs, BIO *); UNREFERENCED(o); TCN_ASSERT(c != NULL); return b != NULL && c->mode != SSL_MODE_CLIENT && SSL_CTX_use_client_CA_bio(c->ctx, b) > 0 ? JNI_TRUE : JNI_FALSE; } TCN_IMPLEMENT_CALL(void, SSLContext, setTmpDHLength)(TCN_STDARGS, jlong ctx, jint length) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); UNREFERENCED(o); TCN_ASSERT(ctx != 0); switch (length) { case 512: SSL_CTX_set_tmp_dh_callback(c->ctx, SSL_callback_tmp_DH_512); return; case 1024: SSL_CTX_set_tmp_dh_callback(c->ctx, SSL_callback_tmp_DH_1024); return; case 2048: SSL_CTX_set_tmp_dh_callback(c->ctx, SSL_callback_tmp_DH_2048); return; case 4096: SSL_CTX_set_tmp_dh_callback(c->ctx, SSL_callback_tmp_DH_4096); return; default: tcn_Throw(e, "Unsupported length %s", length); return; } } TCN_IMPLEMENT_CALL(void, SSLContext, setVerify)(TCN_STDARGS, jlong ctx, jint level, jint depth) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); UNREFERENCED(o); TCN_ASSERT(c != NULL); // No need to set the callback for SSL_CTX_set_verify because we override the default certificate verification via SSL_CTX_set_cert_verify_callback. SSL_CTX_set_verify(c->ctx, tcn_set_verify_config(&c->verify_config, level, depth), NULL); SSL_CTX_set_verify_depth(c->ctx, c->verify_config.verify_depth); } static EVP_PKEY *load_pem_key(tcn_ssl_ctxt_t *c, const char *file) { BIO *bio = NULL; EVP_PKEY *key = NULL; if ((bio = BIO_new(BIO_s_file())) == NULL) { return NULL; } if (BIO_read_filename(bio, file) <= 0) { BIO_free(bio); return NULL; } key = PEM_read_bio_PrivateKey(bio, NULL, (pem_password_cb *)SSL_password_callback, (void *)c->password); BIO_free(bio); return key; } static X509 *load_pem_cert(tcn_ssl_ctxt_t *c, const char *file) { BIO *bio = NULL; X509 *cert = NULL; if ((bio = BIO_new(BIO_s_file())) == NULL) { return NULL; } if (BIO_read_filename(bio, file) <= 0) { BIO_free(bio); return NULL; } cert = PEM_read_bio_X509_AUX(bio, NULL, (pem_password_cb *)SSL_password_callback, (void *)c->password); if (cert == NULL && (ERR_GET_REASON(ERR_peek_last_error()) == PEM_R_NO_START_LINE)) { ERR_clear_error(); BIO_ctrl(bio, BIO_CTRL_RESET, 0, NULL); cert = d2i_X509_bio(bio, NULL); } BIO_free(bio); return cert; } static int ssl_load_pkcs12(tcn_ssl_ctxt_t *c, const char *file, EVP_PKEY **pkey, X509 **cert, STACK_OF(X509) **ca) { const char *pass; char buff[PEM_BUFSIZE]; int len, rc = 0; PKCS12 *p12; BIO *in; if ((in = BIO_new(BIO_s_file())) == 0) return 0; if (BIO_read_filename(in, file) <= 0) { BIO_free(in); return 0; } p12 = d2i_PKCS12_bio(in, 0); if (p12 == 0) { /* Error loading PKCS12 file */ goto cleanup; } /* See if an empty password will do */ if (PKCS12_verify_mac(p12, "", 0) || PKCS12_verify_mac(p12, 0, 0)) { pass = ""; } else { len = SSL_password_callback(buff, PEM_BUFSIZE, 0, (void *) c->password); if (len < 0) { /* Passpharse callback error */ goto cleanup; } if (!PKCS12_verify_mac(p12, buff, len)) { /* Mac verify error (wrong password?) in PKCS12 file */ goto cleanup; } pass = buff; } rc = PKCS12_parse(p12, pass, pkey, cert, ca); cleanup: if (p12 != 0) PKCS12_free(p12); BIO_free(in); return rc; } static void free_and_reset_pass(tcn_ssl_ctxt_t *c, char* old_password, const jboolean rv) { if (!rv) { if (c->password != NULL) { free(c->password); c->password = NULL; } // Restore old password c->password = old_password; } else if (old_password != NULL) { free(old_password); } } TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCertificate)(TCN_STDARGS, jlong ctx, jstring cert, jstring key, jstring password) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jboolean rv = JNI_TRUE; TCN_ALLOC_CSTRING(cert); TCN_ALLOC_CSTRING(key); TCN_ALLOC_CSTRING(password); EVP_PKEY *pkey = NULL; X509 *xcert = NULL; const char *key_file, *cert_file; const char *p; char *old_password = NULL; char err[256]; UNREFERENCED(o); TCN_ASSERT(ctx != 0); if (J2S(password)) { old_password = c->password; c->password = strdup(cpassword); if (c->password == NULL) { rv = JNI_FALSE; goto cleanup; } } key_file = J2S(key); cert_file = J2S(cert); if (!key_file) key_file = cert_file; if (!key_file || !cert_file) { tcn_Throw(e, "No Certificate file specified or invalid file format"); rv = JNI_FALSE; goto cleanup; } if ((p = strrchr(cert_file, '.')) != NULL && strcmp(p, ".pkcs12") == 0) { if (!ssl_load_pkcs12(c, cert_file, &pkey, &xcert, 0)) { ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Unable to load certificate %s (%s)", cert_file, err); rv = JNI_FALSE; goto cleanup; } } else { if ((pkey = load_pem_key(c, key_file)) == NULL) { ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Unable to load certificate key %s (%s)", key_file, err); rv = JNI_FALSE; goto cleanup; } if ((xcert = load_pem_cert(c, cert_file)) == NULL) { ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Unable to load certificate %s (%s)", cert_file, err); rv = JNI_FALSE; goto cleanup; } } if (SSL_CTX_use_certificate(c->ctx, xcert) <= 0) { ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Error setting certificate (%s)", err); rv = JNI_FALSE; goto cleanup; } if (SSL_CTX_use_PrivateKey(c->ctx, pkey) <= 0) { ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Error setting private key (%s)", err); rv = JNI_FALSE; goto cleanup; } if (SSL_CTX_check_private_key(c->ctx) <= 0) { ERR_error_string(ERR_get_error(), err); tcn_Throw(e, "Private key does not match the certificate public key (%s)", err); rv = JNI_FALSE; goto cleanup; } cleanup: TCN_FREE_CSTRING(cert); TCN_FREE_CSTRING(key); TCN_FREE_CSTRING(password); EVP_PKEY_free(pkey); // this function is safe to call with NULL X509_free(xcert); // this function is safe to call with NULL free_and_reset_pass(c, old_password, rv); return rv; } TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCertificateBio)(TCN_STDARGS, jlong ctx, jlong cert, jlong key, jstring password) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); BIO *cert_bio = J2P(cert, BIO *); BIO *key_bio = J2P(key, BIO *); EVP_PKEY *pkey = NULL; X509 *xcert = NULL; jboolean rv = JNI_TRUE; TCN_ALLOC_CSTRING(password); char *old_password = NULL; char err[256]; UNREFERENCED(o); TCN_ASSERT(ctx != 0); if (J2S(password)) { old_password = c->password; c->password = strdup(cpassword); if (c->password == NULL) { rv = JNI_FALSE; goto cleanup; } } if (!key) key = cert; if (!cert || !key) { tcn_Throw(e, "No Certificate file specified or invalid file format"); rv = JNI_FALSE; goto cleanup; } if ((pkey = load_pem_key_bio(c->password, key_bio)) == NULL) { ERR_error_string(ERR_get_error(), err); ERR_clear_error(); tcn_Throw(e, "Unable to load certificate key (%s)",err); rv = JNI_FALSE; goto cleanup; } if ((xcert = load_pem_cert_bio(c->password, cert_bio)) == NULL) { ERR_error_string(ERR_get_error(), err); ERR_clear_error(); tcn_Throw(e, "Unable to load certificate (%s) ", err); rv = JNI_FALSE; goto cleanup; } if (SSL_CTX_use_certificate(c->ctx, xcert) <= 0) { ERR_error_string(ERR_get_error(), err); ERR_clear_error(); tcn_Throw(e, "Error setting certificate (%s)", err); rv = JNI_FALSE; goto cleanup; } if (SSL_CTX_use_PrivateKey(c->ctx, pkey) <= 0) { ERR_error_string(ERR_get_error(), err); ERR_clear_error(); tcn_Throw(e, "Error setting private key (%s)", err); rv = JNI_FALSE; goto cleanup; } if (SSL_CTX_check_private_key(c->ctx) <= 0) { ERR_error_string(ERR_get_error(), err); ERR_clear_error(); tcn_Throw(e, "Private key does not match the certificate public key (%s)", err); rv = JNI_FALSE; goto cleanup; } cleanup: TCN_FREE_CSTRING(password); EVP_PKEY_free(pkey); // this function is safe to call with NULL X509_free(xcert); // this function is safe to call with NULL free_and_reset_pass(c, old_password, rv); return rv; } // Convert protos to wire format static int initProtocols(JNIEnv *e, unsigned char **proto_data, unsigned int *proto_len, jobjectArray protos) { int i; unsigned char *p_data; // We start with allocate 128 bytes which should be good enough for most use-cases while still be pretty low. // We will call realloc to increase this if needed. size_t p_data_size = 128; size_t p_data_len = 0; jstring proto_string; const char *proto_chars; size_t proto_chars_len; int cnt; if (protos == NULL) { // Guard against NULL protos. return -1; } cnt = (*e)->GetArrayLength(e, protos); if (cnt == 0) { // if cnt is 0 we not need to continue and can just fail fast. return -1; } p_data = (unsigned char *) malloc(p_data_size); if (p_data == NULL) { // Not enough memory? return -1; } for (i = 0; i < cnt; ++i) { proto_string = (jstring) (*e)->GetObjectArrayElement(e, protos, i); proto_chars = (*e)->GetStringUTFChars(e, proto_string, 0); proto_chars_len = strlen(proto_chars); if (proto_chars_len > 0 && proto_chars_len <= MAX_ALPN_NPN_PROTO_SIZE) { // We need to add +1 as each protocol is prefixed by it's length (unsigned char). // For all except of the last one we already have the extra space as everything is // delimited by ','. p_data_len += 1 + proto_chars_len; if (p_data_len > p_data_size) { // double size p_data_size <<= 1; p_data = realloc(p_data, p_data_size); if (p_data == NULL) { // Not enough memory? (*e)->ReleaseStringUTFChars(e, proto_string, proto_chars); break; } } // Write the length of the protocol and then increment before memcpy the protocol itself. *p_data = proto_chars_len; ++p_data; memcpy(p_data, proto_chars, proto_chars_len); p_data += proto_chars_len; } // Release the string to prevent memory leaks (*e)->ReleaseStringUTFChars(e, proto_string, proto_chars); } if (p_data == NULL) { // Something went wrong so update the proto_len and return -1 *proto_len = 0; return -1; } else { if (*proto_data != NULL) { // Free old data free(*proto_data); } // Decrement pointer again as we incremented it while creating the protocols in wire format. p_data -= p_data_len; *proto_data = p_data; *proto_len = p_data_len; return 0; } } TCN_IMPLEMENT_CALL(void, SSLContext, setNpnProtos)(TCN_STDARGS, jlong ctx, jobjectArray next_protos, jint selectorFailureBehavior) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); TCN_ASSERT(ctx != 0); UNREFERENCED(o); if (initProtocols(e, &c->next_proto_data, &c->next_proto_len, next_protos) == 0) { c->next_selector_failure_behavior = selectorFailureBehavior; // depending on if it's client mode or not we need to call different functions. if (c->mode == SSL_MODE_CLIENT) { SSL_CTX_set_next_proto_select_cb(c->ctx, SSL_callback_select_next_proto, (void *)c); } else { SSL_CTX_set_next_protos_advertised_cb(c->ctx, SSL_callback_next_protos, (void *)c); } } } TCN_IMPLEMENT_CALL(void, SSLContext, setAlpnProtos)(TCN_STDARGS, jlong ctx, jobjectArray alpn_protos, jint selectorFailureBehavior) { // Only supported with GCC #if !defined(OPENSSL_IS_BORINGSSL) && (defined(__GNUC__) || defined(__GNUG__)) if (!SSL_CTX_set_alpn_protos || !SSL_CTX_set_alpn_select_cb) { UNREFERENCED_STDARGS; UNREFERENCED(ctx); UNREFERENCED(alpn_protos); return; } #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__) tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); TCN_ASSERT(ctx != 0); UNREFERENCED(o); if (initProtocols(e, &c->alpn_proto_data, &c->alpn_proto_len, alpn_protos) == 0) { c->alpn_selector_failure_behavior = selectorFailureBehavior; // depending on if it's client mode or not we need to call different functions. if (c->mode == SSL_MODE_CLIENT) { SSL_CTX_set_alpn_protos(c->ctx, c->alpn_proto_data, c->alpn_proto_len); } else { SSL_CTX_set_alpn_select_cb(c->ctx, SSL_callback_alpn_select_proto, (void *) c); } } #else UNREFERENCED_STDARGS; UNREFERENCED(ctx); UNREFERENCED(alpn_protos); #endif } TCN_IMPLEMENT_CALL(jlong, SSLContext, setSessionCacheMode)(TCN_STDARGS, jlong ctx, jlong mode) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); return SSL_CTX_set_session_cache_mode(c->ctx, mode); } TCN_IMPLEMENT_CALL(jlong, SSLContext, getSessionCacheMode)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); return SSL_CTX_get_session_cache_mode(c->ctx); } TCN_IMPLEMENT_CALL(jlong, SSLContext, setSessionCacheTimeout)(TCN_STDARGS, jlong ctx, jlong timeout) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = SSL_CTX_set_timeout(c->ctx, timeout); return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, getSessionCacheTimeout)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); return SSL_CTX_get_timeout(c->ctx); } TCN_IMPLEMENT_CALL(jlong, SSLContext, setSessionCacheSize)(TCN_STDARGS, jlong ctx, jlong size) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = 0; // Also allow size of 0 which is unlimited if (size >= 0) { SSL_CTX_set_session_cache_mode(c->ctx, SSL_SESS_CACHE_SERVER); rv = SSL_CTX_sess_set_cache_size(c->ctx, size); } return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, getSessionCacheSize)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); return SSL_CTX_sess_get_cache_size(c->ctx); } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionNumber)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = SSL_CTX_sess_number(c->ctx); return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionConnect)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = SSL_CTX_sess_connect(c->ctx); return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionConnectGood)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = SSL_CTX_sess_connect_good(c->ctx); return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionConnectRenegotiate)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = SSL_CTX_sess_connect_renegotiate(c->ctx); return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionAccept)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = SSL_CTX_sess_accept(c->ctx); return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionAcceptGood)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = SSL_CTX_sess_accept_good(c->ctx); return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionAcceptRenegotiate)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = SSL_CTX_sess_accept_renegotiate(c->ctx); return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionHits)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = SSL_CTX_sess_hits(c->ctx); return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionCbHits)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = SSL_CTX_sess_cb_hits(c->ctx); return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionMisses)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = SSL_CTX_sess_misses(c->ctx); return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionTimeouts)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = SSL_CTX_sess_timeouts(c->ctx); return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionCacheFull)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = SSL_CTX_sess_cache_full(c->ctx); return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionTicketKeyNew)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = apr_atomic_read32(&c->ticket_keys_new); return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionTicketKeyResume)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = apr_atomic_read32(&c->ticket_keys_resume); return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionTicketKeyRenew)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = apr_atomic_read32(&c->ticket_keys_renew); return rv; } TCN_IMPLEMENT_CALL(jlong, SSLContext, sessionTicketKeyFail)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jlong rv = apr_atomic_read32(&c->ticket_keys_fail); return rv; } static int current_session_key(tcn_ssl_ctxt_t *c, tcn_ssl_ticket_key_t *key) { int result = JNI_FALSE; apr_thread_rwlock_rdlock(c->mutex); if (c->ticket_keys_len > 0) { *key = c->ticket_keys[0]; result = JNI_TRUE; } apr_thread_rwlock_unlock(c->mutex); return result; } static int find_session_key(tcn_ssl_ctxt_t *c, unsigned char key_name[16], tcn_ssl_ticket_key_t *key, int *is_current_key) { int result = JNI_FALSE; int i; apr_thread_rwlock_rdlock(c->mutex); for (i = 0; i < c->ticket_keys_len; ++i) { // Check if we have a match for tickets. if (memcmp(c->ticket_keys[i].key_name, key_name, 16) == 0) { *key = c->ticket_keys[i]; result = JNI_TRUE; *is_current_key = (i == 0); break; } } apr_thread_rwlock_unlock(c->mutex); return result; } static int ssl_tlsext_ticket_key_cb(SSL *s, unsigned char key_name[16], unsigned char *iv, EVP_CIPHER_CTX *ctx, HMAC_CTX *hctx, int enc) { tcn_ssl_ctxt_t *c = SSL_get_app_data2(s); tcn_ssl_ticket_key_t key; int is_current_key; if (enc) { /* create new session */ if (current_session_key(c, &key)) { if (RAND_bytes(iv, EVP_MAX_IV_LENGTH) <= 0) { return -1; /* insufficient random */ } memcpy(key_name, key.key_name, 16); EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key.aes_key, iv); HMAC_Init_ex(hctx, key.hmac_key, 16, EVP_sha256(), NULL); apr_atomic_inc32(&c->ticket_keys_new); return 1; } // No ticket configured return 0; } else { /* retrieve session */ if (find_session_key(c, key_name, &key, &is_current_key)) { HMAC_Init_ex(hctx, key.hmac_key, 16, EVP_sha256(), NULL); EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key.aes_key, iv ); if (!is_current_key) { // The ticket matched a key in the list, and we want to upgrade it to the current // key. apr_atomic_inc32(&c->ticket_keys_renew); return 2; } // The ticket matched the current key. apr_atomic_inc32(&c->ticket_keys_resume); return 1; } // No matching ticket. apr_atomic_inc32(&c->ticket_keys_fail); return 0; } } TCN_IMPLEMENT_CALL(void, SSLContext, setSessionTicketKeys0)(TCN_STDARGS, jlong ctx, jbyteArray keys) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); jbyte* b; jbyte* key; tcn_ssl_ticket_key_t* ticket_keys; int i; int cnt; cnt = (*e)->GetArrayLength(e, keys) / SSL_SESSION_TICKET_KEY_SIZE; b = (*e)->GetByteArrayElements(e, keys, NULL); ticket_keys = malloc(sizeof(tcn_ssl_ticket_key_t) * cnt); for (i = 0; i < cnt; ++i) { key = b + (SSL_SESSION_TICKET_KEY_SIZE * i); memcpy(ticket_keys[i].key_name, key, 16); memcpy(ticket_keys[i].hmac_key, key + 16, 16); memcpy(ticket_keys[i].aes_key, key + 32, 16); } (*e)->ReleaseByteArrayElements(e, keys, b, 0); apr_thread_rwlock_wrlock(c->mutex); if (c->ticket_keys) { free(c->ticket_keys); } c->ticket_keys_len = cnt; c->ticket_keys = ticket_keys; apr_thread_rwlock_unlock(c->mutex); SSL_CTX_set_tlsext_ticket_key_cb(c->ctx, ssl_tlsext_ticket_key_cb); } static const char* authentication_method(const SSL* ssl) { { const STACK_OF(SSL_CIPHER) *ciphers = NULL; switch (SSL_version(ssl)) { case SSL2_VERSION: return SSL_TXT_RSA; default: ciphers = SSL_get_ciphers(ssl); if (ciphers == NULL || sk_SSL_CIPHER_num(ciphers) <= 0) { // No cipher available so return UNKNOWN. return TCN_UNKNOWN_AUTH_METHOD; } return SSL_cipher_authentication_method(sk_SSL_CIPHER_value(ciphers, 0)); } } } /* Android end */ static int SSL_cert_verify(X509_STORE_CTX *ctx, void *arg) { /* Get Apache context back through OpenSSL context */ SSL *ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); TCN_ASSERT(ssl != NULL); tcn_ssl_ctxt_t *c = SSL_get_app_data2(ssl); TCN_ASSERT(c != NULL); tcn_ssl_verify_config_t* verify_config = SSL_get_app_data4(ssl); TCN_ASSERT(verify_config != NULL); // Get a stack of all certs in the chain STACK_OF(X509) *sk = X509_STORE_CTX_get0_untrusted(ctx); // SSL_CTX_set_verify_depth() and SSL_set_verify_depth() set the limit up to which depth certificates in a chain are // used during the verification procedure. If the certificate chain is longer than allowed, the certificates above // the limit are ignored. Error messages are generated as if these certificates would not be present, // most likely a X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY will be issued. // https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_verify.html const int totalQueuedLength = sk_X509_num(sk); int len = TCN_MIN(verify_config->verify_depth, totalQueuedLength); unsigned i; X509 *cert; int length; unsigned char *buf; JNIEnv *e; jbyteArray array; jbyteArray bArray; const char *authMethod; jstring authMethodString; jint result; jclass byteArrayClass = tcn_get_byte_array_class(); tcn_get_java_env(&e); // Create the byte[][] array that holds all the certs array = (*e)->NewObjectArray(e, len, byteArrayClass, NULL); for(i = 0; i < len; i++) { cert = sk_X509_value(sk, i); buf = NULL; length = i2d_X509(cert, &buf); if (length < 0) { // In case of error just return an empty byte[][] array = (*e)->NewObjectArray(e, 0, byteArrayClass, NULL); if (buf != NULL) { // We need to delete the local references so we not leak memory as this method is called via callback. OPENSSL_free(buf); } break; } bArray = (*e)->NewByteArray(e, length); (*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); } authMethod = authentication_method(ssl); authMethodString = (*e)->NewStringUTF(e, authMethod); result = (*e)->CallIntMethod(e, c->verifier, c->verifier_method, P2J(ssl), array, authMethodString); #ifdef X509_V_ERR_UNSPECIFIED // If we failed to verify for an unknown reason (currently this happens if we can't find a common root) then we should // fail with the same status as recommended in the OpenSSL docs https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_verify.html if (result == X509_V_ERR_UNSPECIFIED && len < totalQueuedLength) { result = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY; } #else // HACK! // LibreSSL 2.4.x doesn't support the X509_V_ERR_UNSPECIFIED so we introduce a work around to make sure a supported alert is used. // This should be reverted when we support LibreSSL 2.5.x (which does support X509_V_ERR_UNSPECIFIED). if (result == TCN_X509_V_ERR_UNSPECIFIED) { result = len < totalQueuedLength ? X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY : X509_V_ERR_CERT_REJECTED; } #endif // TODO(scott): if verify_config->verify_depth == SSL_CVERIFY_OPTIONAL we have the option to let the handshake // succeed for some of the "informational" error messages (e.g. X509_V_ERR_EMAIL_MISMATCH ?) // Set the correct error so it will be included in the alert. X509_STORE_CTX_set_error(ctx, result); // We need to delete the local references so we not leak memory as this method is called via callback. (*e)->DeleteLocalRef(e, authMethodString); (*e)->DeleteLocalRef(e, array); return result == X509_V_OK ? 1 : 0; } TCN_IMPLEMENT_CALL(void, SSLContext, setCertVerifyCallback)(TCN_STDARGS, jlong ctx, jobject verifier) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); UNREFERENCED(o); TCN_ASSERT(ctx != 0); if (verifier == NULL) { SSL_CTX_set_cert_verify_callback(c->ctx, NULL, NULL); } else { jclass verifier_class = (*e)->GetObjectClass(e, verifier); jmethodID method = (*e)->GetMethodID(e, verifier_class, "verify", "(J[[BLjava/lang/String;)I"); if (method == NULL) { return; } // Delete the reference to the previous specified verifier if needed. if (c->verifier != NULL) { (*e)->DeleteLocalRef(e, c->verifier); } c->verifier = (*e)->NewGlobalRef(e, verifier); c->verifier_method = method; SSL_CTX_set_cert_verify_callback(c->ctx, SSL_cert_verify, NULL); } } /** * Returns an array containing all the X500 principal's bytes. * * Partly based on code from conscrypt: * https://android.googlesource.com/platform/external/conscrypt/+/master/src/main/native/org_conscrypt_NativeCrypto.cpp */ static jobjectArray principalBytes(JNIEnv* e, const STACK_OF(X509_NAME)* names) { jobjectArray array; jbyteArray bArray; int i; int count; int length; unsigned char *buf; X509_NAME* principal; jclass byteArrayClass = tcn_get_byte_array_class(); if (names == NULL) { return NULL; } count = sk_X509_NAME_num(names); if (count <= 0) { return NULL; } array = (*e)->NewObjectArray(e, count, byteArrayClass, NULL); if (array == NULL) { return NULL; } for (i = 0; i < count; i++) { principal = sk_X509_NAME_value(names, i); buf = NULL; length = i2d_X509_NAME(principal, &buf); if (length < 0) { if (buf != NULL) { // We need to delete the local references so we not leak memory as this method is called via callback. OPENSSL_free(buf); } // In case of error just return an empty byte[][] return (*e)->NewObjectArray(e, 0, byteArrayClass, NULL); } bArray = (*e)->NewByteArray(e, length); (*e)->SetByteArrayRegion(e, bArray, 0, length, (jbyte*) buf); (*e)->SetObjectArrayElement(e, array, i, bArray); // 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; } static int cert_requested(SSL* ssl, X509** x509Out, EVP_PKEY** pkeyOut) { #if defined(LIBRESSL_VERSION_NUMBER) return -1; #else tcn_ssl_ctxt_t *c = SSL_get_app_data2(ssl); int ctype_num; jbyte* ctype_bytes; jobjectArray issuers; JNIEnv *e; jbyteArray keyTypes; jobject keyMaterial; STACK_OF(X509) *chain = NULL; X509 *cert = NULL; EVP_PKEY* pkey = NULL; jlong certChain; jlong privateKey; int certChainLen; int i; tcn_get_java_env(&e); #if OPENSSL_VERSION_NUMBER < 0x10002000L char ssl2_ctype = SSL3_CT_RSA_SIGN; switch (ssl->version) { case SSL2_VERSION: ctype_bytes = (jbyte*) &ssl2_ctype; ctype_num = 1; break; case SSL3_VERSION: case TLS1_VERSION: case TLS1_1_VERSION: case TLS1_2_VERSION: case DTLS1_VERSION: ctype_bytes = (jbyte*) ssl->s3->tmp.ctype; ctype_num = ssl->s3->tmp.ctype_num; break; } #else ctype_num = SSL_get0_certificate_types(ssl, (const uint8_t **) &ctype_bytes); #endif if (ctype_num <= 0) { // Use no certificate return 0; } keyTypes = (*e)->NewByteArray(e, ctype_num); if (keyTypes == NULL) { // Something went seriously wrong, bail out! return -1; } (*e)->SetByteArrayRegion(e, keyTypes, 0, ctype_num, ctype_bytes); issuers = principalBytes(e, SSL_get_client_CA_list(ssl)); // Execute the java callback keyMaterial = (*e)->CallObjectMethod(e, c->cert_requested_callback, c->cert_requested_callback_method, P2J(ssl), keyTypes, issuers); if (keyMaterial == NULL) { return 0; } // Any failure after this line must cause a goto fail to cleanup things. certChain = (*e)->GetLongField(e, keyMaterial, tcn_get_key_material_certificate_chain_field()); privateKey = (*e)->GetLongField(e, keyMaterial, tcn_get_key_material_private_key_field()); chain = J2P(certChain, STACK_OF(X509) *); pkey = J2P(privateKey, EVP_PKEY *); if (chain == NULL || pkey == NULL) { goto fail; } certChainLen = sk_X509_num(chain); if (certChainLen <= 0) { goto fail; } // Skip the first cert in the chain as we will write this to x509Out. // See https://github.com/netty/netty-tcnative/issues/184 for (i = 1; i < certChainLen; ++i) { // We need to explicit add extra certs to the chain as stated in: // https://www.openssl.org/docs/manmaster/ssl/SSL_CTX_set_client_cert_cb.html // // Using SSL_add0_chain_cert(...) here as we not want to increment the reference count. if (SSL_add0_chain_cert(ssl, sk_X509_value(chain, i)) <= 0) { goto fail; } } cert = sk_X509_value(chain, 0); // Increment the reference count as we already set the chain via SSL_set0_chain(...) and using a cert out of it. if (tcn_X509_up_ref(cert) <= 0) { goto fail; } *x509Out = cert; *pkeyOut = pkey; // Free the stack it self but not the certs. sk_X509_free(chain); return 1; fail: ERR_clear_error(); sk_X509_pop_free(chain, X509_free); EVP_PKEY_free(pkey); // TODO: Would it be more correct to return 0 in this case we may not want to use any cert / private key ? return -1; #endif /* defined(LIBRESSL_VERSION_NUMBER) */ } TCN_IMPLEMENT_CALL(void, SSLContext, setCertRequestedCallback)(TCN_STDARGS, jlong ctx, jobject callback) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); UNREFERENCED(o); TCN_ASSERT(ctx != 0); if (callback == NULL) { SSL_CTX_set_client_cert_cb(c->ctx, NULL); } else { jclass callback_class = (*e)->GetObjectClass(e, callback); jmethodID method = (*e)->GetMethodID(e, callback_class, "requested", "(J[B[[B)Lio/netty/internal/tcnative/CertificateRequestedCallback$KeyMaterial;"); if (method == NULL) { return; } // Delete the reference to the previous specified verifier if needed. if (c->cert_requested_callback != NULL) { (*e)->DeleteLocalRef(e, c->cert_requested_callback); } c->cert_requested_callback = (*e)->NewGlobalRef(e, callback); c->cert_requested_callback_method = method; SSL_CTX_set_client_cert_cb(c->ctx, cert_requested); } } TCN_IMPLEMENT_CALL(jboolean, SSLContext, setSessionIdContext)(TCN_STDARGS, jlong ctx, jbyteArray sidCtx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); int len = (*e)->GetArrayLength(e, sidCtx); unsigned char *buf; int res; UNREFERENCED(o); TCN_ASSERT(ctx != 0); buf = malloc(len); (*e)->GetByteArrayRegion(e, sidCtx, 0, len, (jbyte*) buf); res = SSL_CTX_set_session_id_context(c->ctx, buf, len); free(buf); if (res == 1) { return JNI_TRUE; } return JNI_FALSE; } TCN_IMPLEMENT_CALL(jint, SSLContext, setMode)(TCN_STDARGS, jlong ctx, jint mode) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); UNREFERENCED(o); TCN_ASSERT(ctx != 0); return (jint) SSL_CTX_set_mode(c->ctx, mode); } TCN_IMPLEMENT_CALL(jint, SSLContext, getMode)(TCN_STDARGS, jlong ctx) { tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); UNREFERENCED(o); TCN_ASSERT(ctx != 0); return (jint) SSL_CTX_get_mode(c->ctx); }