You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by jf...@apache.org on 2015/06/18 17:55:06 UTC

svn commit: r1686258 - in /tomcat/native/trunk/native: include/ssl_private.h src/sslcontext.c src/sslutils.c

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-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org