blob: c1ff8fe5f76ebb927ab03c7bd599e132dcf21df1 [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.
*/
/** SSL Context wrapper
*
* @author Mladen Turk
* @version $Revision$, $Date$
*/
#include "tcn.h"
#include "apr_file_io.h"
#include "apr_thread_mutex.h"
#include "apr_poll.h"
#ifdef HAVE_OPENSSL
#include "ssl_private.h"
static apr_status_t ssl_context_cleanup(void *data)
{
tcn_ssl_ctxt_t *c = (tcn_ssl_ctxt_t *)data;
if (c) {
int i;
if (c->crl)
X509_STORE_free(c->crl);
c->crl = NULL;
if (c->ctx)
SSL_CTX_free(c->ctx);
c->ctx = NULL;
for (i = 0; i < SSL_AIDX_MAX; i++) {
if (c->certs[i]) {
X509_free(c->certs[i]);
c->certs[i] = NULL;
}
if (c->keys[i]) {
EVP_PKEY_free(c->keys[i]);
c->keys[i] = NULL;
}
}
if (c->bio_is) {
SSL_BIO_close(c->bio_is);
c->bio_is = NULL;
}
if (c->bio_os) {
SSL_BIO_close(c->bio_os);
c->bio_os = NULL;
}
}
return APR_SUCCESS;
}
/* Initialize server context */
TCN_IMPLEMENT_CALL(jlong, SSLContext, make)(TCN_STDARGS, jlong pool,
jint protocol, jint mode)
{
apr_pool_t *p = J2P(pool, apr_pool_t *);
tcn_ssl_ctxt_t *c = NULL;
SSL_CTX *ctx = NULL;
UNREFERENCED(o);
switch (protocol) {
case SSL_PROTOCOL_SSLV2:
case SSL_PROTOCOL_SSLV2 | SSL_PROTOCOL_TLSV1:
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;
case SSL_PROTOCOL_SSLV3:
case SSL_PROTOCOL_SSLV3 | SSL_PROTOCOL_TLSV1:
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;
case SSL_PROTOCOL_SSLV2 | SSL_PROTOCOL_SSLV3:
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:
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;
}
if (!ctx) {
tcn_ThrowException(e, "Invalid Server SSL Protocol");
goto init_failed;
}
if ((c = apr_pcalloc(p, sizeof(tcn_ssl_ctxt_t))) == NULL) {
tcn_ThrowAPRException(e, apr_get_os_error());
goto init_failed;
}
c->protocol = protocol;
c->mode = mode;
c->ctx = ctx;
c->pool = p;
c->bio_os = BIO_new(BIO_s_file());
if (c->bio_os != NULL)
BIO_set_fp(c->bio_os, stderr, BIO_NOCLOSE | BIO_FP_TEXT);
SSL_CTX_set_options(c->ctx, SSL_OP_ALL);
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);
/*
* Configure additional context ingredients
*/
SSL_CTX_set_options(c->ctx, SSL_OP_SINGLE_DH_USE);
#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
/*
* 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);
#endif
/* Default session context id and cache size */
SSL_CTX_sess_set_cache_size(c->ctx, SSL_DEFAULT_CACHE_SIZE);
MD5((const unsigned char *)SSL_DEFAULT_VHOST_NAME,
(unsigned long)(sizeof(SSL_DEFAULT_VHOST_NAME) - 1),
&(c->context_id[0]));
if (mode) {
SSL_CTX_set_tmp_rsa_callback(c->ctx, SSL_callback_tmp_RSA);
SSL_CTX_set_tmp_dh_callback(c->ctx, SSL_callback_tmp_DH);
}
/* Set default Certificate verification level
* and depth for the Client Authentication
*/
c->verify_depth = 1;
c->verify_mode = SSL_CVERIFY_UNSET;
c->shutdown_type = SSL_SHUTDOWN_TYPE_UNSET;
/* 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 *)(&tcn_password_callback));
/*
* 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);
init_failed:
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 */
return apr_pool_cleanup_run(c->pool, c, ssl_context_cleanup);
}
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)) {
MD5((const unsigned char *)J2S(id),
(unsigned long)strlen(J2S(id)),
&(c->context_id[0]));
}
TCN_FREE_CSTRING(id);
}
TCN_IMPLEMENT_CALL(void, SSLContext, setBIO)(TCN_STDARGS, jlong ctx,
jlong bio, jint dir)
{
tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
BIO *bio_handle = J2P(bio, BIO *);
UNREFERENCED_STDARGS;
TCN_ASSERT(ctx != 0);
if (dir == 0) {
if (c->bio_os && c->bio_os != bio_handle)
SSL_BIO_close(c->bio_os);
c->bio_os = bio_handle;
}
else if (dir == 1) {
if (c->bio_is && c->bio_is != bio_handle)
SSL_BIO_close(c->bio_is);
c->bio_is = bio_handle;
}
else
return;
SSL_BIO_doref(bio_handle);
}
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(void, SSLContext, setQuietShutdown)(TCN_STDARGS, jlong ctx,
jboolean mode)
{
tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
UNREFERENCED_STDARGS;
TCN_ASSERT(ctx != 0);
SSL_CTX_set_quiet_shutdown(c->ctx, mode ? 1 : 0);
}
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, setCARevocation)(TCN_STDARGS, jlong ctx,
jstring file,
jstring path)
{
tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
TCN_ALLOC_CSTRING(file);
TCN_ALLOC_CSTRING(path);
jboolean rv = JNI_FALSE;
X509_LOOKUP *lookup;
char err[256];
UNREFERENCED(o);
TCN_ASSERT(ctx != 0);
if (J2S(file) == NULL && J2S(path) == NULL)
return JNI_FALSE;
if (!c->crl) {
if ((c->crl = X509_STORE_new()) == NULL)
goto cleanup;
}
if (J2S(file)) {
lookup = X509_STORE_add_lookup(c->crl, X509_LOOKUP_file());
if (lookup == NULL) {
ERR_error_string(ERR_get_error(), err);
X509_STORE_free(c->crl);
c->crl = NULL;
tcn_Throw(e, "Lookup failed for file %s (%s)", J2S(file), err);
goto cleanup;
}
X509_LOOKUP_load_file(lookup, J2S(file), X509_FILETYPE_PEM);
}
if (J2S(path)) {
lookup = X509_STORE_add_lookup(c->crl, X509_LOOKUP_hash_dir());
if (lookup == NULL) {
ERR_error_string(ERR_get_error(), err);
X509_STORE_free(c->crl);
c->crl = NULL;
tcn_Throw(e, "Lookup failed for path %s (%s)", J2S(file), err);
goto cleanup;
}
X509_LOOKUP_add_dir(lookup, J2S(path), X509_FILETYPE_PEM);
}
rv = JNI_TRUE;
cleanup:
TCN_FREE_CSTRING(file);
TCN_FREE_CSTRING(path);
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, setCACertificate)(TCN_STDARGS,
jlong ctx,
jstring file,
jstring path)
{
tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
jboolean rv = JNI_TRUE;
TCN_ALLOC_CSTRING(file);
TCN_ALLOC_CSTRING(path);
UNREFERENCED(o);
TCN_ASSERT(ctx != 0);
if (file == NULL && path == NULL)
return JNI_FALSE;
/*
* Configure Client Authentication details
*/
if (!SSL_CTX_load_verify_locations(c->ctx,
J2S(file), J2S(path))) {
char err[256];
ERR_error_string(ERR_get_error(), err);
tcn_Throw(e, "Unable to configure locations "
"for client authentication (%s)", err);
rv = JNI_FALSE;
goto cleanup;
}
c->store = SSL_CTX_get_cert_store(c->ctx);
if (c->mode) {
STACK_OF(X509_NAME) *ca_certs;
c->ca_certs++;
ca_certs = SSL_CTX_get_client_CA_list(c->ctx);
if (ca_certs == NULL) {
SSL_load_client_CA_file(J2S(file));
if (ca_certs != NULL)
SSL_CTX_set_client_CA_list(c->ctx, (STACK *)ca_certs);
}
else {
if (!SSL_add_file_cert_subjects_to_stack((STACK *)ca_certs, J2S(file)))
ca_certs = NULL;
}
if (ca_certs == NULL && c->verify_mode == SSL_CVERIFY_REQUIRE) {
/*
* Give a warning when no CAs were configured but client authentication
* should take place. This cannot work.
*/
BIO_printf(c->bio_os,
"[WARN] Oops, you want to request client "
"authentication, but no CAs are known for "
"verification!?");
}
}
cleanup:
TCN_FREE_CSTRING(file);
TCN_FREE_CSTRING(path);
return rv;
}
TCN_IMPLEMENT_CALL(void, SSLContext, setShutdownType)(TCN_STDARGS, jlong ctx,
jint type)
{
tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
UNREFERENCED_STDARGS;
TCN_ASSERT(ctx != 0);
c->shutdown_type = type;
}
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 *);
int verify = SSL_VERIFY_NONE;
UNREFERENCED(o);
TCN_ASSERT(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_CTX_set_verify(c->ctx, verify, SSL_callback_SSL_verify);
}
static EVP_PKEY *load_pem_key(tcn_ssl_ctxt_t *c, const char *file)
{
BIO *bio = NULL;
EVP_PKEY *key = NULL;
tcn_pass_cb_t *cb_data = c->cb_data;
int i;
if ((bio = BIO_new(BIO_s_file())) == NULL) {
return NULL;
}
if (BIO_read_filename(bio, file) <= 0) {
BIO_free(bio);
return NULL;
}
if (!cb_data)
cb_data = &tcn_password_callback;
for (i = 0; i < 3; i++) {
key = PEM_read_bio_PrivateKey(bio, NULL,
(pem_password_cb *)SSL_password_callback,
(void *)cb_data);
if (key)
break;
cb_data->password[0] = '\0';
BIO_ctrl(bio, BIO_CTRL_RESET, 0, NULL);
}
BIO_free(bio);
return key;
}
static X509 *load_pem_cert(tcn_ssl_ctxt_t *c, const char *file)
{
BIO *bio = NULL;
X509 *cert = NULL;
tcn_pass_cb_t *cb_data = c->cb_data;
int i;
if ((bio = BIO_new(BIO_s_file())) == NULL) {
return NULL;
}
if (BIO_read_filename(bio, file) <= 0) {
BIO_free(bio);
return NULL;
}
for (i = 0; i < 3; i++) {
cert = PEM_read_bio_X509_AUX(bio, NULL,
(pem_password_cb *)SSL_password_callback,
(void *)cb_data);
if (cert)
break;
cb_data->password[0] = '\0';
BIO_ctrl(bio, BIO_CTRL_RESET, 0, NULL);
}
BIO_free(bio);
return cert;
}
TCN_IMPLEMENT_CALL(void, SSLContext, setRandom)(TCN_STDARGS, jlong ctx,
jstring file)
{
tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
TCN_ALLOC_CSTRING(file);
TCN_ASSERT(ctx != 0);
UNREFERENCED(o);
if (J2S(file))
c->rand_file = apr_pstrdup(c->pool, J2S(file));
TCN_FREE_CSTRING(file);
}
TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCertificate)(TCN_STDARGS, jlong ctx,
jstring cert, jstring key,
jstring password, jint idx)
{
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);
const char *key_file, *cert_file;
char err[256];
UNREFERENCED(o);
TCN_ASSERT(ctx != 0);
if (idx < 0 || idx >= SSL_AIDX_MAX) {
/* TODO: Throw something */
rv = JNI_FALSE;
goto cleanup;
}
if (J2S(password)) {
if (!c->cb_data)
c->cb_data = &tcn_password_callback;
strncpy(c->cb_data->password, J2S(password), SSL_MAX_PASSWORD_LEN);
c->cb_data->password[SSL_MAX_PASSWORD_LEN-1] = '\0';
}
key_file = J2S(key);
cert_file = J2S(cert);
if (!key_file)
key_file = cert_file;
if (!key_file) {
tcn_Throw(e, "No Certificate file specified");
rv = JNI_FALSE;
goto cleanup;
}
if ((c->keys[idx] = 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 ((c->certs[idx] = 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, c->certs[idx]) <= 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, c->keys[idx]) <= 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);
return rv;
}
#else
/* OpenSSL is not supported.
* Create empty stubs.
*/
TCN_IMPLEMENT_CALL(jlong, SSLContext, make)(TCN_STDARGS, jlong pool,
jint protocol, jint mode)
{
UNREFERENCED_STDARGS;
UNREFERENCED(pool);
UNREFERENCED(protocol);
UNREFERENCED(mode);
return 0;
}
TCN_IMPLEMENT_CALL(jint, SSLContext, free)(TCN_STDARGS, jlong ctx)
{
UNREFERENCED_STDARGS;
UNREFERENCED(ctx);
return APR_ENOTIMPL;
}
TCN_IMPLEMENT_CALL(void, SSLContext, setContextId)(TCN_STDARGS, jlong ctx,
jstring id)
{
UNREFERENCED_STDARGS;
UNREFERENCED(ctx);
UNREFERENCED(id);
}
TCN_IMPLEMENT_CALL(void, SSLContext, setBIO)(TCN_STDARGS, jlong ctx,
jlong bio, jint dir)
{
UNREFERENCED_STDARGS;
UNREFERENCED(ctx);
UNREFERENCED(bio);
UNREFERENCED(dir);
}
TCN_IMPLEMENT_CALL(void, SSLContext, setOptions)(TCN_STDARGS, jlong ctx,
jint opt)
{
UNREFERENCED_STDARGS;
UNREFERENCED(ctx);
UNREFERENCED(opt);
}
TCN_IMPLEMENT_CALL(void, SSLContext, setQuietShutdown)(TCN_STDARGS, jlong ctx,
jboolean mode)
{
UNREFERENCED_STDARGS;
UNREFERENCED(ctx);
UNREFERENCED(mode);
}
TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCipherSuite)(TCN_STDARGS, jlong ctx,
jstring ciphers)
{
UNREFERENCED_STDARGS;
UNREFERENCED(ctx);
UNREFERENCED(ciphers);
return JNI_FALSE;
}
TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCARevocation)(TCN_STDARGS, jlong ctx,
jstring file,
jstring path)
{
UNREFERENCED_STDARGS;
UNREFERENCED(ctx);
UNREFERENCED(file);
UNREFERENCED(path);
return JNI_FALSE;
}
TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCertificateChainFile)(TCN_STDARGS, jlong ctx,
jstring file,
jboolean skipfirst)
{
UNREFERENCED_STDARGS;
UNREFERENCED(ctx);
UNREFERENCED(file);
UNREFERENCED(skipfirst);
return JNI_FALSE;
}
TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCACertificate)(TCN_STDARGS,
jlong ctx,
jstring file,
jstring path)
{
UNREFERENCED_STDARGS;
UNREFERENCED(ctx);
UNREFERENCED(file);
UNREFERENCED(path);
return JNI_FALSE;
}
TCN_IMPLEMENT_CALL(void, SSLContext, setShutdownType)(TCN_STDARGS, jlong ctx,
jint type)
{
UNREFERENCED_STDARGS;
UNREFERENCED(ctx);
UNREFERENCED(type);
}
TCN_IMPLEMENT_CALL(void, SSLContext, setVerify)(TCN_STDARGS, jlong ctx,
jint level, jint depth)
{
UNREFERENCED_STDARGS;
UNREFERENCED(ctx);
UNREFERENCED(level);
UNREFERENCED(depth);
}
TCN_IMPLEMENT_CALL(void, SSLContext, setRandom)(TCN_STDARGS, jlong ctx,
jstring file)
{
UNREFERENCED_STDARGS;
UNREFERENCED(ctx);
UNREFERENCED(file);
}
TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCertificate)(TCN_STDARGS, jlong ctx,
jstring cert, jstring key,
jstring password, jint idx)
{
UNREFERENCED_STDARGS;
UNREFERENCED(ctx);
UNREFERENCED(cert);
UNREFERENCED(key);
UNREFERENCED(password);
UNREFERENCED(idx);
return JNI_FALSE;
}
#endif