blob: e2d341c3089ad6ea55c5992a82740505336d6217 [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
*/
#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);
if (protocol == SSL_PROTOCOL_TLSV1_2) {
#ifdef SSL_OP_NO_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());
#endif
} else if (protocol == SSL_PROTOCOL_TLSV1_1) {
#ifdef SSL_OP_NO_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());
#endif
} 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());
} else 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());
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) && !defined(OPENSSL_NO_SSL2)
} else 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());
#endif
#ifndef SSL_OP_NO_TLSv1_2
} else if (protocol & SSL_PROTOCOL_TLSV1_2) {
/* requested but not supported */
#endif
#ifndef SSL_OP_NO_TLSv1_1
} else if (protocol & SSL_PROTOCOL_TLSV1_1) {
/* requested but not supported */
#endif
} else {
#if OPENSSL_VERSION_NUMBER < 0x10100000L
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());
#else
if (mode == SSL_MODE_CLIENT)
ctx = SSL_CTX_new(TLS_client_method());
else if (mode == SSL_MODE_SERVER)
ctx = SSL_CTX_new(TLS_server_method());
else
ctx = SSL_CTX_new(TLS_method());
#endif
}
if (!ctx) {
char err[TCN_OPENSSL_ERROR_STRING_LENGTH];
ERR_error_string_n(ERR_get_error(), err, TCN_OPENSSL_ERROR_STRING_LENGTH);
tcn_Throw(e, "Invalid Server SSL Protocol (%s)", err);
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);
#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
#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);
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);
/* 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));
SSL_CTX_set_info_callback(c->ctx, SSL_callback_handshake);
/*
* 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)) {
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, 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);
#ifndef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
/* Clear the flag if not supported */
if (opt & 0x00040000)
opt &= ~0x00040000;
#endif
SSL_CTX_set_options(c->ctx, opt);
}
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(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;
#ifndef HAVE_EXPORT_CIPHERS
size_t len;
char *buf;
#endif
UNREFERENCED(o);
TCN_ASSERT(ctx != 0);
if (!J2S(ciphers))
return JNI_FALSE;
#ifndef HAVE_EXPORT_CIPHERS
/*
* Always disable NULL and export ciphers,
* no matter what was given in the config.
*/
len = strlen(J2S(ciphers)) + strlen(SSL_CIPHERS_ALWAYS_DISABLED) + 1;
buf = malloc(len * sizeof(char *));
if (buf == NULL)
return JNI_FALSE;
memcpy(buf, SSL_CIPHERS_ALWAYS_DISABLED, strlen(SSL_CIPHERS_ALWAYS_DISABLED));
memcpy(buf + strlen(SSL_CIPHERS_ALWAYS_DISABLED), J2S(ciphers), strlen(J2S(ciphers)));
buf[len - 1] = '\0';
if (!SSL_CTX_set_cipher_list(c->ctx, buf)) {
#else
if (!SSL_CTX_set_cipher_list(c->ctx, J2S(ciphers))) {
#endif
char err[TCN_OPENSSL_ERROR_STRING_LENGTH];
ERR_error_string_n(ERR_get_error(), err, TCN_OPENSSL_ERROR_STRING_LENGTH);
tcn_Throw(e, "Unable to configure permitted SSL ciphers (%s)", err);
rv = JNI_FALSE;
}
#ifndef HAVE_EXPORT_CIPHERS
free(buf);
#endif
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[TCN_OPENSSL_ERROR_STRING_LENGTH];
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_n(ERR_get_error(), err, TCN_OPENSSL_ERROR_STRING_LENGTH);
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_n(ERR_get_error(), err, TCN_OPENSSL_ERROR_STRING_LENGTH);
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[TCN_OPENSSL_ERROR_STRING_LENGTH];
ERR_error_string_n(ERR_get_error(), err, TCN_OPENSSL_ERROR_STRING_LENGTH);
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, ca_certs);
}
else {
if (!SSL_add_file_cert_subjects_to_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;
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;
cert = PEM_read_bio_X509_AUX(bio, NULL,
(pem_password_cb *)SSL_password_callback,
(void *)cb_data);
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;
tcn_pass_cb_t *cb_data = c->cb_data;
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 {
if (!cb_data)
cb_data = &tcn_password_callback;
len = SSL_password_callback(buff, PEM_BUFSIZE, 0, cb_data);
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;
}
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;
const char *p;
char err[TCN_OPENSSL_ERROR_STRING_LENGTH];
#ifdef HAVE_ECC
EC_GROUP *ecparams;
int nid;
EC_KEY *eckey = NULL;
#endif
DH *dhparams;
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 || !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, &c->keys[idx], &c->certs[idx], 0)) {
ERR_error_string_n(ERR_get_error(), err, TCN_OPENSSL_ERROR_STRING_LENGTH);
tcn_Throw(e, "Unable to load certificate %s (%s)",
cert_file, err);
rv = JNI_FALSE;
goto cleanup;
}
}
else {
if ((c->keys[idx] = load_pem_key(c, key_file)) == NULL) {
ERR_error_string_n(ERR_get_error(), err, TCN_OPENSSL_ERROR_STRING_LENGTH);
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_n(ERR_get_error(), err, TCN_OPENSSL_ERROR_STRING_LENGTH);
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_n(ERR_get_error(), err, TCN_OPENSSL_ERROR_STRING_LENGTH);
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_n(ERR_get_error(), err, TCN_OPENSSL_ERROR_STRING_LENGTH);
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_n(ERR_get_error(), err, TCN_OPENSSL_ERROR_STRING_LENGTH);
tcn_Throw(e, "Private key does not match the certificate public key (%s)",
err);
rv = JNI_FALSE;
goto cleanup;
}
/*
* Try to read DH parameters from the (first) SSLCertificateFile
*/
/* XXX Does this also work for pkcs12 or only for PEM files?
* If only for PEM files move above to the PEM handling */
if ((idx == 0) && (dhparams = SSL_dh_GetParamFromFile(cert_file))) {
SSL_CTX_set_tmp_dh(c->ctx, dhparams);
}
#ifdef HAVE_ECC
/*
* Similarly, try to read the ECDH curve name from SSLCertificateFile...
*/
/* XXX Does this also work for pkcs12 or only for PEM files?
* If only for PEM files move above to the PEM handling */
if ((ecparams = SSL_ec_GetParamFromFile(cert_file)) &&
(nid = EC_GROUP_get_curve_name(ecparams)) &&
(eckey = EC_KEY_new_by_curve_name(nid))) {
SSL_CTX_set_tmp_ecdh(c->ctx, eckey);
}
/*
* ...otherwise, configure NIST P-256 (required to enable ECDHE)
*/
else {
#if defined(SSL_CTX_set_ecdh_auto)
SSL_CTX_set_ecdh_auto(c->ctx, 1);
#else
eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
SSL_CTX_set_tmp_ecdh(c->ctx, eckey);
#endif
}
EC_KEY_free(eckey);
#endif
SSL_CTX_set_tmp_dh_callback(c->ctx, SSL_callback_tmp_DH);
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, clearOptions)(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