Return-Path: X-Original-To: apmail-httpd-cvs-archive@www.apache.org Delivered-To: apmail-httpd-cvs-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 352E917A34 for ; Tue, 31 Mar 2015 19:12:10 +0000 (UTC) Received: (qmail 63763 invoked by uid 500); 31 Mar 2015 19:12:10 -0000 Delivered-To: apmail-httpd-cvs-archive@httpd.apache.org Received: (qmail 63701 invoked by uid 500); 31 Mar 2015 19:12:10 -0000 Mailing-List: contact cvs-help@httpd.apache.org; run by ezmlm Precedence: bulk Reply-To: dev@httpd.apache.org list-help: list-unsubscribe: List-Post: List-Id: Delivered-To: mailing list cvs@httpd.apache.org Received: (qmail 63692 invoked by uid 99); 31 Mar 2015 19:12:10 -0000 Received: from eris.apache.org (HELO hades.apache.org) (140.211.11.105) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 31 Mar 2015 19:12:10 +0000 Received: from hades.apache.org (localhost [127.0.0.1]) by hades.apache.org (ASF Mail Server at hades.apache.org) with ESMTP id E68EFAC006A for ; Tue, 31 Mar 2015 19:12:09 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1670434 - in /httpd/httpd/trunk/modules/ssl: mod_ssl.c ssl_engine_init.c ssl_engine_kernel.c ssl_private.h Date: Tue, 31 Mar 2015 19:12:09 -0000 To: cvs@httpd.apache.org From: jim@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20150331191209.E68EFAC006A@hades.apache.org> Author: jim Date: Tue Mar 31 19:12:08 2015 New Revision: 1670434 URL: http://svn.apache.org/r1670434 Log: More ALPN goodness Modified: httpd/httpd/trunk/modules/ssl/mod_ssl.c httpd/httpd/trunk/modules/ssl/ssl_engine_init.c httpd/httpd/trunk/modules/ssl/ssl_engine_kernel.c httpd/httpd/trunk/modules/ssl/ssl_private.h Modified: httpd/httpd/trunk/modules/ssl/mod_ssl.c URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/ssl/mod_ssl.c?rev=1670434&r1=1670433&r2=1670434&view=diff ============================================================================== --- httpd/httpd/trunk/modules/ssl/mod_ssl.c (original) +++ httpd/httpd/trunk/modules/ssl/mod_ssl.c Tue Mar 31 19:12:08 2015 @@ -483,7 +483,7 @@ static int modssl_register_alpn(conn_rec ssl_alpn_propose_protos advertisefn, ssl_alpn_proto_negotiated negotiatedfn) { -#if defined(HAVE_TLS_ALPN) || defined(HAVE_TLS_NPN) +#ifdef HAVE_TLS_ALPN SSLConnRec *sslconn = myConnConfig(c); if (!sslconn) { Modified: httpd/httpd/trunk/modules/ssl/ssl_engine_init.c URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/ssl/ssl_engine_init.c?rev=1670434&r1=1670433&r2=1670434&view=diff ============================================================================== --- httpd/httpd/trunk/modules/ssl/ssl_engine_init.c (original) +++ httpd/httpd/trunk/modules/ssl/ssl_engine_init.c Tue Mar 31 19:12:08 2015 @@ -645,6 +645,11 @@ static void ssl_init_ctx_callbacks(serve SSL_CTX_set_info_callback(ctx, ssl_callback_Info); +#ifdef HAVE_TLS_ALPN + SSL_CTX_set_alpn_select_cb( + ctx, ssl_callback_alpn_select, NULL); +#endif + #ifdef HAVE_TLS_NPN SSL_CTX_set_next_protos_advertised_cb( ctx, ssl_callback_AdvertiseNextProtos, NULL); Modified: httpd/httpd/trunk/modules/ssl/ssl_engine_kernel.c URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/ssl/ssl_engine_kernel.c?rev=1670434&r1=1670433&r2=1670434&view=diff ============================================================================== --- httpd/httpd/trunk/modules/ssl/ssl_engine_kernel.c (original) +++ httpd/httpd/trunk/modules/ssl/ssl_engine_kernel.c Tue Mar 31 19:12:08 2015 @@ -2162,7 +2162,151 @@ int ssl_callback_SessionTicket(SSL *ssl, } #endif /* HAVE_TLS_SESSION_TICKETS */ -#ifdef HAVE_TLS_NPN +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; +} + +#ifdef HAVE_TLS_ALPN +/* + * Compare to ALPN protocol proposal. Result is similar to strcmp(): + * 0 gives same precedence, >0 means proto1 is prefered. + */ +static int ssl_cmp_alpn_protos(modssl_ctx_t *ctx, + const char *proto1, + const char *proto2) +{ + /* TODO: we should have a mod_ssl configuration parameter. */ + if (ctx && ctx->ssl_alpn_pref) { + int index1 = ssl_array_index(ctx->ssl_alpn_pref, proto1); + int index2 = ssl_array_index(ctx->ssl_alpn_pref, proto2); + if (index2 > index1) { + return (index1 >= 0)? 1 : -1; + } + else if (index1 > index2) { + return (index2 >= 0)? -1 : 1; + } + } + /* both have the same index (mabye -1 or no pref configured) and we 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 ssl_callback_alpn_select(SSL *ssl, + const unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, void *arg) +{ + conn_rec *c = (conn_rec*)SSL_get_app_data(ssl); + SSLConnRec *sslconn = myConnConfig(c); + server_rec *s = mySrvFromConn(c); + SSLSrvConfigRec *sc = mySrvConfig(s); + modssl_ctx_t *mctx = myCtxConfig(sslconn, sc); + const char *alpn_http1 = "http/1.1"; + apr_array_header_t *client_protos; + apr_array_header_t *proposed_protos; + int i; + + /* If the connection object is not available, + * then there's nothing for us to do. */ + if (c == NULL) { + return SSL_TLSEXT_ERR_OK; + } + + if (inlen == 0) { + // someone tries to trick us? + ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO() + "alpn client protocol list empty"); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + client_protos = apr_array_make(c->pool, 0, sizeof(char *)); + for (i = 0; i < inlen; /**/) { + unsigned int plen = in[i++]; + if (plen + i > inlen) { + // someone tries to trick us? + ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO() + "alpn protocol identier too long"); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + APR_ARRAY_PUSH(client_protos, char*) = + apr_pstrndup(c->pool, (const char *)in+i, plen); + i += plen; + } + + /* Regardless of installed hooks, the http/1.1 protocol is always + * supported by us. Add it to the proposals if the client also + * offers it. */ + proposed_protos = apr_array_make(c->pool, client_protos->nelts+1, + sizeof(char *)); + if (ssl_array_index(client_protos, alpn_http1) >= 0) { + APR_ARRAY_PUSH(proposed_protos, const char*) = alpn_http1; + } + + if (sslconn->alpn_proposefns != NULL) { + /* Invoke our alpn_propos_proto hooks, giving other modules a chance to + * propose protocol names for selection. We might have several such + * hooks installed and if two make a proposal, we need to give + * preference to one. + */ + for (i = 0; i < sslconn->alpn_proposefns->nelts; i++) { + ssl_alpn_propose_protos fn = + APR_ARRAY_IDX(sslconn->alpn_proposefns, i, + ssl_alpn_propose_protos); + + if (fn(c, client_protos, proposed_protos) == DONE) + break; + } + } + + if (proposed_protos->nelts <= 0) { + ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO() + "none of the client alpn protocols are supported"); + 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(mctx, (const char *)*out, proto) < 0) { + *out = (const unsigned char*)proto; + } + } + + size_t len = strlen((const char*)*out); + if (len > 255) { + ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO() + "alpn negotiated protocol name too long"); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + *outlen = (unsigned char)len; + + return SSL_TLSEXT_ERR_OK; +} +#endif +#if defined(HAVE_TLS_NPN) /* * This callback function is executed when SSL needs to decide what protocols * to advertise during Next Protocol Negotiation (NPN). It must produce a Modified: httpd/httpd/trunk/modules/ssl/ssl_private.h URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/ssl/ssl_private.h?rev=1670434&r1=1670433&r2=1670434&view=diff ============================================================================== --- httpd/httpd/trunk/modules/ssl/ssl_private.h (original) +++ httpd/httpd/trunk/modules/ssl/ssl_private.h Tue Mar 31 19:12:08 2015 @@ -454,8 +454,8 @@ typedef struct { apr_array_header_t *npn_negofns; /* list of ssl_npn_proto_negotiated callbacks. */ #endif -#if defined(HAVE_TLS_ALPN) || defined(HAVE_TLS_NPN) - /* Poor man's inter-module optional hooks for NPN. */ +#ifdef HAVE_TLS_ALPN + /* Poor man's inter-module optional hooks for ALPN. */ apr_array_header_t *alpn_proposefns; /* list of ssl_alpn_propose_protos callbacks */ apr_array_header_t *alpn_negofns; /* list of ssl_alpn_proto_negotiated callbacks. */ #endif @@ -821,14 +821,13 @@ int ssl_callback_ServerNameIndi int ssl_callback_SessionTicket(SSL *, unsigned char *, unsigned char *, EVP_CIPHER_CTX *, HMAC_CTX *, int); #endif -int ssl_callback_AdvertiseNextProtos(SSL *ssl, const unsigned char **data, unsigned int *len, void *arg); #ifdef HAVE_TLS_ALPN int ssl_callback_alpn_select(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg); #endif -#if defined(HAVE_TLS_NPN) +#ifdef HAVE_TLS_NPN int ssl_callback_AdvertiseNextProtos(SSL *ssl, const unsigned char **data, unsigned int *len, void *arg); #endif