Author: markt Date: Wed Apr 8 18:51:17 2015 New Revision: 1672140 URL: http://svn.apache.org/r1672140 Log: First pass at native changes required to support ALPN.
Removed: tomcat/native/trunk/native/src/sslext.c Modified: tomcat/native/trunk/native/include/ssl_private.h tomcat/native/trunk/native/src/sslcontext.c tomcat/native/trunk/native/src/sslnetwork.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=1672140&r1=1672139&r2=1672140&view=diff ============================================================================== --- tomcat/native/trunk/native/include/ssl_private.h (original) +++ tomcat/native/trunk/native/include/ssl_private.h Wed Apr 8 18:51:17 2015 @@ -256,10 +256,11 @@ struct tcn_ssl_ctxt_t { int verify_mode; tcn_pass_cb_t *cb_data; - /* for client: send request NPN. - * for server: accept requested NPN. + /* for client: List of protocols to request via ALPN. + * for server: List of protocols to accept via ALPN. */ - char *npn; + char *alpn; + int alpnlen; }; Modified: tomcat/native/trunk/native/src/sslcontext.c URL: http://svn.apache.org/viewvc/tomcat/native/trunk/native/src/sslcontext.c?rev=1672140&r1=1672139&r2=1672140&view=diff ============================================================================== --- tomcat/native/trunk/native/src/sslcontext.c (original) +++ tomcat/native/trunk/native/src/sslcontext.c Wed Apr 8 18:51:17 2015 @@ -629,6 +629,153 @@ cleanup: return rv; } +static int ssl_array_index(apr_array_header_t *array, + const char *s) +{ + int i; + for (i = 0; i < array->nelts; i++) { + const char *p = APR_ARRAY_IDX(array, i, const char*); + if (!strcmp(p, s)) { + return i; + } + } + return -1; +} + +static int ssl_cmp_alpn_protos(apr_array_header_t *array, + const char *proto1, + const char *proto2) +{ + int index1 = ssl_array_index(array, proto1); + int index2 = ssl_array_index(array, proto2); + if (index2 > index1) { + return (index1 >= 0)? 1 : -1; + } + else if (index1 > index2) { + return (index2 >= 0)? -1 : 1; + } + + /* Both have the same index (-1 so neither listed by cient) compare + * the names so that spdy3 gets precedence over spdy2. That makes + * the outcome at least deterministic. */ + return strcmp((const char *)proto1, (const char *)proto2); +} + +/* + * This callback function is executed when the TLS Application Layer + * Protocol Negotiate Extension (ALPN, RFC 7301) is triggered by the client + * hello, giving a list of desired protocol names (in descending preference) + * to the server. + * The callback has to select a protocol name or return an error if none of + * the clients preferences is supported. + * The selected protocol does not have to be on the client list, according + * to RFC 7301, so no checks are performed. + * The client protocol list is serialized as length byte followed by ascii + * characters (not null-terminated), followed by the next protocol name. + */ +int cb_server_alpn(SSL *ssl, + const unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, void *arg) +{ + tcn_ssl_ctxt_t *tcsslctx = (tcn_ssl_ctxt_t *)arg; + tcn_ssl_conn_t *con = (tcn_ssl_conn_t *)SSL_get_app_data(ssl); + apr_array_header_t *client_protos; + apr_array_header_t *proposed_protos; + int i; + unsigned short splen; + + printf("inlen [%d]\n", inlen); + + if (inlen == 0) { + // Client specified an empty protocol list. Nothing to negotiate. + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + client_protos = apr_array_make(con->pool , 0, sizeof(char *)); + for (i = 0; i < inlen; /**/) { + unsigned int plen = in[i++]; + if (plen + i > inlen) { + // The protocol name extends beyond the declared length + // of the protocol list. + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + APR_ARRAY_PUSH(client_protos, char*) = apr_pstrndup(con->pool, (const char *)in+i, plen); + i += plen; + } + + if (tcsslctx->alpn == NULL) { + // Server supported protocol names not set. + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + if (tcsslctx->alpnlen == 0) { + // Server supported protocols is an empty list + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + printf("A\n"); + + proposed_protos = apr_array_make(con->pool, 0, sizeof(char *)); + for (i = 0; i < tcsslctx->alpnlen; /**/) { + unsigned int plen = tcsslctx->alpn[i++]; + if (plen + i > tcsslctx->alpnlen) { + // The protocol name extends beyond the declared length + // of the protocol list. + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + APR_ARRAY_PUSH(proposed_protos, char*) = apr_pstrndup(con->pool, (const char *)tcsslctx->alpn+i, plen); + i += plen; + } + + printf("E\n"); + + if (proposed_protos->nelts <= 0) { + // Should never happen. The server did not specify any protocols. + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + /* Now select the most preferred protocol from the proposals. */ + *out = APR_ARRAY_IDX(proposed_protos, 0, const unsigned char *); + for (i = 1; i < proposed_protos->nelts; ++i) { + const char *proto = APR_ARRAY_IDX(proposed_protos, i, const char*); + /* Do we prefer it over existing candidate? */ + if (ssl_cmp_alpn_protos(client_protos, (const char *)*out, proto) < 0) { + *out = (const unsigned char*)proto; + } + } + + printf("F\n"); + + size_t len = strlen((const char*)*out); + if (len > 255) { + // Agreed protocol name too long + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + *outlen = (unsigned char)len; + + return SSL_TLSEXT_ERR_OK; +} + +TCN_IMPLEMENT_CALL(jint, SSLContext, setALPN)(TCN_STDARGS, jlong ctx, + jbyteArray buf, jint len) +{ + tcn_ssl_ctxt_t *sslctx = J2P(ctx, tcn_ssl_ctxt_t *); + + sslctx->alpn = apr_pcalloc(sslctx->pool, len); + (*e)->GetByteArrayRegion(e, buf, 0, len, &sslctx->alpn[0]); + sslctx->alpnlen = len; + + 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); + return APR_ENOTIMPL; + } + return 0; +} + #else /* OpenSSL is not supported. * Create empty stubs. @@ -773,4 +920,13 @@ TCN_IMPLEMENT_CALL(jboolean, SSLContext, return JNI_FALSE; } +TCN_IMPLEMENT_CALL(jint, SSLExt, setALPN)(TCN_STDARGS, jlong ctx, + jbyteArray buf, jint len) +{ + UNREFERENCED_STDARGS; + UNREFERENCED(ctx); + UNREFERENCED(buf); + UNREFERENCED(len); + return APR_ENOTIMPL; +} #endif Modified: tomcat/native/trunk/native/src/sslnetwork.c URL: http://svn.apache.org/viewvc/tomcat/native/trunk/native/src/sslnetwork.c?rev=1672140&r1=1672139&r2=1672140&view=diff ============================================================================== --- tomcat/native/trunk/native/src/sslnetwork.c (original) +++ tomcat/native/trunk/native/src/sslnetwork.c Wed Apr 8 18:51:17 2015 @@ -686,6 +686,26 @@ TCN_IMPLEMENT_CALL(void, SSLSocket, setV SSL_set_verify(con->ssl, verify, NULL); } +TCN_IMPLEMENT_CALL(jint, SSLSocket, getALPN)(TCN_STDARGS, jlong sock, jbyteArray buf) +{ + const unsigned char *alpn; + unsigned alpn_len; + tcn_socket_t *s = J2P(sock, tcn_socket_t *); + tcn_ssl_conn_t *tcssl = (tcn_ssl_conn_t *)s->opaque; + int bufLen = (*e)->GetArrayLength(e, buf); + + SSL_get0_alpn_selected(tcssl->ssl, &alpn, &alpn_len); + + if (alpn_len == 0 || bufLen < alpn_len) { + return 0; + } + int len = alpn_len; + (*e)->SetByteArrayRegion(e, buf, 0, len, alpn); + + return len; +} + + #else /* OpenSSL is not supported. * Create empty stubs. @@ -715,4 +735,11 @@ TCN_IMPLEMENT_CALL(jint, SSLSocket, rene return (jint)APR_ENOTIMPL; } +TCN_IMPLEMENT_CALL(jint, SSLSocket, getALPN)(TCN_STDARGS, jlong sock, jbyteArray buf) +{ + UNREFERENCED(sock); + UNREFERENCED(buf); + return (jint)APR_ENOTIMPL; +} + #endif --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org