Author: jfclere Date: Thu Jun 18 15:55:06 2015 New Revision: 1686258 URL: http://svn.apache.org/r1686258 Log: Add netty-tcnative methods to sslcontext.c
Modified: tomcat/native/trunk/native/include/ssl_private.h tomcat/native/trunk/native/src/sslcontext.c tomcat/native/trunk/native/src/sslutils.c Modified: tomcat/native/trunk/native/include/ssl_private.h URL: http://svn.apache.org/viewvc/tomcat/native/trunk/native/include/ssl_private.h?rev=1686258&r1=1686257&r2=1686258&view=diff ============================================================================== --- tomcat/native/trunk/native/include/ssl_private.h (original) +++ tomcat/native/trunk/native/include/ssl_private.h Thu Jun 18 15:55:06 2015 @@ -203,6 +203,9 @@ #endif /* !defined(OPENSSL_NO_TLSEXT) && defined(SSL_set_tlsext_host_name) */ +#define MAX_ALPN_NPN_PROTO_SIZE 65535 +#define SSL_SELECTOR_FAILURE_CHOOSE_MY_LAST_PROTOCOL 1 + typedef struct { /* client can have any number of cert/key pairs */ const char *cert_file; @@ -259,6 +262,20 @@ struct tcn_ssl_ctxt_t { */ char *alpn; int alpnlen; + /* Add from netty-tcnative */ + /* certificate verifier callback */ + jobject verifier; + jmethodID verifier_method; + + unsigned char *next_proto_data; + unsigned int next_proto_len; + int next_selector_failure_behavior; + + /* Holds the alpn protocols, each of them prefixed with the len of the protocol */ + unsigned char *alpn_proto_data; + unsigned int alpn_proto_len; + int alpn_selector_failure_behavior; + /* End add from netty-tcnative */ }; @@ -313,5 +330,9 @@ void SSL_callback_handshake(const int SSL_CTX_use_certificate_chain(SSL_CTX *, const char *, int); int SSL_callback_SSL_verify(int, X509_STORE_CTX *); int SSL_rand_seed(const char *file); +int SSL_callback_next_protos(SSL *, const unsigned char **, unsigned int *, void *); +int SSL_callback_select_next_proto(SSL *, unsigned char **, unsigned char *, const unsigned char *, unsigned int,void *); +int SSL_callback_alpn_select_proto(SSL *, const unsigned char **, unsigned char *, const unsigned char *, unsigned int, void *); + #endif /* SSL_PRIVATE_H */ Modified: tomcat/native/trunk/native/src/sslcontext.c URL: http://svn.apache.org/viewvc/tomcat/native/trunk/native/src/sslcontext.c?rev=1686258&r1=1686257&r2=1686258&view=diff ============================================================================== --- tomcat/native/trunk/native/src/sslcontext.c (original) +++ tomcat/native/trunk/native/src/sslcontext.c Thu Jun 18 15:55:06 2015 @@ -26,6 +26,8 @@ #ifdef HAVE_OPENSSL #include "ssl_private.h" +static jclass byteArrayClass; + static apr_status_t ssl_context_cleanup(void *data) { tcn_ssl_ctxt_t *c = (tcn_ssl_ctxt_t *)data; @@ -55,6 +57,26 @@ static apr_status_t ssl_context_cleanup( SSL_BIO_close(c->bio_os); c->bio_os = NULL; } + + if (c->verifier) { + JNIEnv *e; + tcn_get_java_env(&e); + (*e)->DeleteGlobalRef(e, c->verifier); + c->verifier = NULL; + } + c->verifier_method = NULL; + + if (c->next_proto_data) { + free(c->next_proto_data); + c->next_proto_data = NULL; + } + c->next_proto_len = 0; + + if (c->alpn_proto_data) { + free(c->alpn_proto_data); + c->alpn_proto_data = NULL; + } + c->alpn_proto_len = 0; } return APR_SUCCESS; } @@ -67,9 +89,9 @@ static jmethodID sni_java_callback; */ int ssl_callback_ServerNameIndication(SSL *ssl, int *al, tcn_ssl_ctxt_t *c) { - // TODO: Is it better to cache the JNIEnv* during the call to handshake? + /* TODO: Is it better to cache the JNIEnv* during the call to handshake? */ - // Get the JNI environment for this callback + /* Get the JNI environment for this callback */ JavaVM *javavm = tcn_get_java_vm(); JNIEnv *env; const char *servername; @@ -105,7 +127,9 @@ TCN_IMPLEMENT_CALL(jlong, SSLContext, ma apr_pool_t *p = J2P(pool, apr_pool_t *); tcn_ssl_ctxt_t *c = NULL; SSL_CTX *ctx = NULL; + jclass clazz; + UNREFERENCED(o); if (protocol == SSL_PROTOCOL_NONE) { tcn_Throw(e, "No SSL protocols requested"); goto init_failed; @@ -211,6 +235,11 @@ TCN_IMPLEMENT_CALL(jlong, SSLContext, ma #ifdef HAVE_ECC SSL_CTX_set_options(c->ctx, SSL_OP_SINGLE_ECDH_USE); #endif +#ifdef SSL_OP_NO_COMPRESSION + /* Disable SSL compression to be safe */ + SSL_CTX_set_options(c->ctx, SSL_OP_NO_COMPRESSION); +#endif + /** To get back the tomcat wrapper from CTX */ SSL_CTX_set_app_data(c->ctx, (char *)c); @@ -222,8 +251,17 @@ TCN_IMPLEMENT_CALL(jlong, SSLContext, ma */ SSL_CTX_set_options(c->ctx, SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); #endif +#ifdef SSL_MODE_RELEASE_BUFFERS + /* Release idle buffers to the SSL_CTX free list */ + SSL_CTX_set_mode(c->ctx, SSL_MODE_RELEASE_BUFFERS); +#endif /* 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); @@ -258,6 +296,10 @@ TCN_IMPLEMENT_CALL(jlong, SSLContext, ma ssl_context_cleanup, apr_pool_cleanup_null); + /* Cache the byte[].class for performance reasons */ + clazz = (*e)->FindClass(e, "[B"); + byteArrayClass = (jclass) (*e)->NewGlobalRef(e, clazz); + return P2J(c); init_failed: return 0; @@ -326,6 +368,16 @@ TCN_IMPLEMENT_CALL(void, SSLContext, set 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) { @@ -502,11 +554,20 @@ TCN_IMPLEMENT_CALL(jboolean, SSLContext, /* * Give a warning when no CAs were configured but client authentication * should take place. This cannot work. - */ - BIO_printf(c->bio_os, + */ + if (c->bio_os) { + BIO_printf(c->bio_os, + "[WARN] Oops, you want to request client " + "authentication, but no CAs are known for " + "verification!?"); + } + else { + fprintf(stderr, "[WARN] Oops, you want to request client " "authentication, but no CAs are known for " "verification!?"); + } + } } cleanup: @@ -515,6 +576,98 @@ cleanup: return rv; } +TCN_IMPLEMENT_CALL(void, SSLContext, setTmpDH)(TCN_STDARGS, jlong ctx, + jstring file) +{ + tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); + BIO *bio = NULL; + DH *dh = NULL; + TCN_ALLOC_CSTRING(file); + UNREFERENCED(o); + TCN_ASSERT(ctx != 0); + TCN_ASSERT(file); + + if (!J2S(file)) { + tcn_Throw(e, "Error while configuring DH: no dh param file given"); + return; + } + + bio = BIO_new_file(J2S(file), "r"); + if (!bio) { + char err[256]; + ERR_error_string(ERR_get_error(), err); + tcn_Throw(e, "Error while configuring DH using %s: %s", J2S(file), err); + TCN_FREE_CSTRING(file); + return; + } + + dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + BIO_free(bio); + if (!dh) { + char err[256]; + ERR_error_string(ERR_get_error(), err); + tcn_Throw(e, "Error while configuring DH: no DH parameter found in %s (%s)", J2S(file), err); + TCN_FREE_CSTRING(file); + return; + } + + if (1 != SSL_CTX_set_tmp_dh(c->ctx, dh)) { + char err[256]; + DH_free(dh); + ERR_error_string(ERR_get_error(), err); + tcn_Throw(e, "Error while configuring DH with file %s: %s", J2S(file), err); + TCN_FREE_CSTRING(file); + return; + } + + DH_free(dh); + TCN_FREE_CSTRING(file); +} + +TCN_IMPLEMENT_CALL(void, SSLContext, setTmpECDHByCurveName)(TCN_STDARGS, jlong ctx, + jstring curveName) +{ +#ifdef HAVE_ECC + tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); + int i; + EC_KEY *ecdh; + TCN_ALLOC_CSTRING(curveName); + UNREFERENCED(o); + TCN_ASSERT(ctx != 0); + TCN_ASSERT(curveName); + + /* First try to get curve by name */ + i = OBJ_sn2nid(J2S(curveName)); + if (!i) { + tcn_Throw(e, "Can't configure elliptic curve: unknown curve name %s", J2S(curveName)); + TCN_FREE_CSTRING(curveName); + return; + } + + ecdh = EC_KEY_new_by_curve_name(i); + if (!ecdh) { + tcn_Throw(e, "Can't configure elliptic curve: unknown curve name %s", J2S(curveName)); + TCN_FREE_CSTRING(curveName); + return; + } + + /* Setting found curve to context */ + if (1 != SSL_CTX_set_tmp_ecdh(c->ctx, ecdh)) { + char err[256]; + EC_KEY_free(ecdh); + ERR_error_string(ERR_get_error(), err); + tcn_Throw(e, "Error while configuring elliptic curve %s: %s", J2S(curveName), err); + TCN_FREE_CSTRING(curveName); + return; + } + EC_KEY_free(ecdh); + TCN_FREE_CSTRING(curveName); +#else + tcn_Throw(e, "Cant't configure elliptic curve: unsupported by this OpenSSL version"); + return; +#endif +} + TCN_IMPLEMENT_CALL(void, SSLContext, setShutdownType)(TCN_STDARGS, jlong ctx, jint type) { @@ -940,13 +1093,497 @@ TCN_IMPLEMENT_CALL(jint, SSLContext, set if (sslctx->mode == SSL_MODE_SERVER) { SSL_CTX_set_alpn_select_cb(sslctx->ctx, cb_server_alpn, sslctx); } else { - // TODO: Implement client side call-back - // SSL_CTX_set_next_proto_select_cb(sslctx->ctx, cb_request_alpn, sslctx); + /* + * TODO: Implement client side call-back + * SSL_CTX_set_next_proto_select_cb(sslctx->ctx, cb_request_alpn, sslctx); + */ return APR_ENOTIMPL; } return 0; } +/* Start of netty-tc-native add */ + +/* Convert protos to wire format */ +static int initProtocols(JNIEnv *e, const tcn_ssl_ctxt_t *c, 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 increate 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, &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) +{ + tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); + + TCN_ASSERT(ctx != 0); + UNREFERENCED(o); + + if (initProtocols(e, c, &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); + + } + } +} + +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; +} + +#define TICKET_KEYS_SIZE 48 +TCN_IMPLEMENT_CALL(void, SSLContext, setSessionTicketKeys)(TCN_STDARGS, jlong ctx, jbyteArray keys) +{ + tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); + jbyte* b; + + if ((*e)->GetArrayLength(e, keys) != TICKET_KEYS_SIZE) { + if (c->bio_os) { + BIO_printf(c->bio_os, "[ERROR] Session ticket keys provided were wrong size."); + } + else { + fprintf(stderr, "[ERROR] Session ticket keys provided were wrong size."); + } + exit(1); + } + + b = (*e)->GetByteArrayElements(e, keys, NULL); + SSL_CTX_set_tlsext_ticket_keys(c->ctx, b, TICKET_KEYS_SIZE); + (*e)->ReleaseByteArrayElements(e, keys, b, 0); +} + + +/* + * Adapted from OpenSSL: + * http://osxr.org/openssl/source/ssl/ssl_locl.h#0291 + */ +/* Bits for algorithm_mkey (key exchange algorithm) */ +#define SSL_kRSA 0x00000001L /* RSA key exchange */ +#define SSL_kDHr 0x00000002L /* DH cert, RSA CA cert */ /* no such ciphersuites supported! */ +#define SSL_kDHd 0x00000004L /* DH cert, DSA CA cert */ /* no such ciphersuite supported! */ +#define SSL_kEDH 0x00000008L /* tmp DH key no DH cert */ +#define SSL_kKRB5 0x00000010L /* Kerberos5 key exchange */ +#define SSL_kECDHr 0x00000020L /* ECDH cert, RSA CA cert */ +#define SSL_kECDHe 0x00000040L /* ECDH cert, ECDSA CA cert */ +#define SSL_kEECDH 0x00000080L /* ephemeral ECDH */ +#define SSL_kPSK 0x00000100L /* PSK */ +#define SSL_kGOST 0x00000200L /* GOST key exchange */ +#define SSL_kSRP 0x00000400L /* SRP */ + +/* Bits for algorithm_auth (server authentication) */ +#define SSL_aRSA 0x00000001L /* RSA auth */ +#define SSL_aDSS 0x00000002L /* DSS auth */ +#define SSL_aNULL 0x00000004L /* no auth (i.e. use ADH or AECDH) */ +#define SSL_aDH 0x00000008L /* Fixed DH auth (kDHd or kDHr) */ /* no such ciphersuites supported! */ +#define SSL_aECDH 0x00000010L /* Fixed ECDH auth (kECDHe or kECDHr) */ +#define SSL_aKRB5 0x00000020L /* KRB5 auth */ +#define SSL_aECDSA 0x00000040L /* ECDSA auth*/ +#define SSL_aPSK 0x00000080L /* PSK auth */ +#define SSL_aGOST94 0x00000100L /* GOST R 34.10-94 signature auth */ +#define SSL_aGOST01 0x00000200L /* GOST R 34.10-2001 signature auth */ + +/* OpenSSL end */ + +/* + * Adapted from Android: + * https://android.googlesource.com/platform/external/openssl/+/master/patches/0003-jsse.patch + */ +const char* SSL_CIPHER_authentication_method(const SSL_CIPHER* cipher){ + switch (cipher->algorithm_mkey) + { + case SSL_kRSA: + return SSL_TXT_RSA; + case SSL_kDHr: + return SSL_TXT_DH "_" SSL_TXT_RSA; + case SSL_kDHd: + return SSL_TXT_DH "_" SSL_TXT_DSS; + case SSL_kEDH: + switch (cipher->algorithm_auth) + { + case SSL_aDSS: + return "DHE_" SSL_TXT_DSS; + case SSL_aRSA: + return "DHE_" SSL_TXT_RSA; + case SSL_aNULL: + return SSL_TXT_DH "_anon"; + default: + return "UNKNOWN"; + } + case SSL_kKRB5: + return SSL_TXT_KRB5; + case SSL_kECDHr: + return SSL_TXT_ECDH "_" SSL_TXT_RSA; + case SSL_kECDHe: + return SSL_TXT_ECDH "_" SSL_TXT_ECDSA; + case SSL_kEECDH: + switch (cipher->algorithm_auth) + { + case SSL_aECDSA: + return "ECDHE_" SSL_TXT_ECDSA; + case SSL_aRSA: + return "ECDHE_" SSL_TXT_RSA; + case SSL_aNULL: + return SSL_TXT_ECDH "_anon"; + default: + return "UNKNOWN"; + } + default: + return "UNKNOWN"; + } +} + +static const char* SSL_authentication_method(const SSL* ssl) { +{ + switch (ssl->version) + { + case SSL2_VERSION: + return SSL_TXT_RSA; + default: + return SSL_CIPHER_authentication_method(ssl->s3->tmp.new_cipher); + } + } +} +/* 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_ssl_ctxt_t *c = SSL_get_app_data2(ssl); + + + // Get a stack of all certs in the chain + STACK_OF(X509) *sk = ctx->untrusted; + + int len = sk_X509_num(sk); + unsigned i; + X509 *cert; + int length; + unsigned char *buf; + JNIEnv *e; + jbyteArray array; + jbyteArray bArray; + const char *authMethod; + jstring authMethodString; + jboolean result; + int r; + 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 = (X509*) 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); + // 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 = SSL_authentication_method(ssl); + authMethodString = (*e)->NewStringUTF(e, authMethod); + + result = (*e)->CallBooleanMethod(e, c->verifier, c->verifier_method, P2J(ssl), array, + authMethodString); + + r = result == JNI_TRUE ? 1 : 0; + + // 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 r; +} + + +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;)Z"); + + 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); + } +} + +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; +} + +/* End of netty-tc-native add */ #else /* OpenSSL is not supported. * Create empty stubs. Modified: tomcat/native/trunk/native/src/sslutils.c URL: http://svn.apache.org/viewvc/tomcat/native/trunk/native/src/sslutils.c?rev=1686258&r1=1686257&r2=1686258&view=diff ============================================================================== --- tomcat/native/trunk/native/src/sslutils.c (original) +++ tomcat/native/trunk/native/src/sslutils.c Thu Jun 18 15:55:06 2015 @@ -562,6 +562,87 @@ void SSL_callback_handshake(const SSL *s } +int SSL_callback_next_protos(SSL *ssl, const unsigned char **data, + unsigned int *len, void *arg) +{ + tcn_ssl_ctxt_t *ssl_ctxt = arg; + + *data = ssl_ctxt->next_proto_data; + *len = ssl_ctxt->next_proto_len; + + return SSL_TLSEXT_ERR_OK; +} + +/* The code here is inspired by nghttp2 + * + * See https://github.com/tatsuhiro-t/nghttp2/blob/ae0100a9abfcf3149b8d9e62aae216e946b517fb/src/shrpx_ssl.cc#L244 */ +int select_next_proto(SSL *ssl, const unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, unsigned char *supported_protos, + unsigned int supported_protos_len, int failure_behavior) { + + unsigned int i = 0; + unsigned char target_proto_len; + const unsigned char *p; + const unsigned char *end; + const unsigned char *proto; + unsigned char proto_len; + + while (i < supported_protos_len) { + target_proto_len = *supported_protos; + ++supported_protos; + + p = in; + end = in + inlen; + + while (p < end) { + proto_len = *p; + proto = ++p; + + if (proto + proto_len <= end && target_proto_len == proto_len && + memcmp(supported_protos, proto, proto_len) == 0) { + + // We found a match, so set the output and return with OK! + *out = proto; + *outlen = proto_len; + + return SSL_TLSEXT_ERR_OK; + } + // Move on to the next protocol. + p += proto_len; + } + + // increment len and pointers. + i += target_proto_len; + supported_protos += target_proto_len; + } + + if (failure_behavior == SSL_SELECTOR_FAILURE_CHOOSE_MY_LAST_PROTOCOL) { + // There were no match but we just select our last protocol and hope the other peer support it. + // + // decrement the pointer again so the pointer points to the start of the protocol. + p -= proto_len; + *out = p; + *outlen = proto_len; + return SSL_TLSEXT_ERR_OK; + } + // TODO: OpenSSL currently not support to fail with fatal error. Once this changes we can also support it here. + // Issue https://github.com/openssl/openssl/issues/188 has been created for this. + // Nothing matched so not select anything and just accept. + return SSL_TLSEXT_ERR_NOACK; +} + +int SSL_callback_select_next_proto(SSL *ssl, unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, + void *arg) { + tcn_ssl_ctxt_t *ssl_ctxt = arg; + return select_next_proto(ssl, (const unsigned char **) out, outlen, in, inlen, ssl_ctxt->next_proto_data, ssl_ctxt->next_proto_len, ssl_ctxt->next_selector_failure_behavior); +} + +int SSL_callback_alpn_select_proto(SSL* ssl, const unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, void *arg) { + tcn_ssl_ctxt_t *ssl_ctxt = arg; + return select_next_proto(ssl, out, outlen, in, inlen, ssl_ctxt->alpn_proto_data, ssl_ctxt->alpn_proto_len, ssl_ctxt->alpn_selector_failure_behavior); +} #ifdef HAVE_OCSP_STAPLING /* Function that is used to do the OCSP verification */ --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org