vapier      15/06/01 06:08:35

  Added:                apache-2.4.12-alpn.patch
  Log:
  Add ALPN support via USE=alpn #471512.
  
  (Portage version: 2.2.20/cvs/Linux x86_64, signed Manifest commit with key 
D2E96200)

Revision  Changes    Path
1.1                  www-servers/apache/files/apache-2.4.12-alpn.patch

file : 
http://sources.gentoo.org/viewvc.cgi/gentoo-x86/www-servers/apache/files/apache-2.4.12-alpn.patch?rev=1.1&view=markup
plain: 
http://sources.gentoo.org/viewvc.cgi/gentoo-x86/www-servers/apache/files/apache-2.4.12-alpn.patch?rev=1.1&content-type=text/plain

Index: apache-2.4.12-alpn.patch
===================================================================
https://bugs.gentoo.org/471512

upstream apache has merged alpn into trunk:
https://issues.apache.org/bugzilla/show_bug.cgi?id=52210
note: the bug is closed INVALID due to the npn discussion; go to the bottom to
see alpn merged into it trunk.  unfortunately, it wasn't merged into the 2.4
branch.

the mod_h2 project has backported it to the 2.4 branch:
https://github.com/icing/mod_h2/tree/master/sandbox/httpd/patches
commit 73e4d0e9c813b58581a32a6948780fa948094cc1

--- modules/ssl/mod_ssl.c
+++ modules/ssl/mod_ssl.c
@@ -273,6 +273,12 @@
                 "OpenSSL configuration command")
 #endif
 
+#ifdef HAVE_TLS_ALPN
+    SSL_CMD_SRV(ALPNPreference, ITERATE,
+                "Preference in Application-Layer Protocol Negotiation (ALPN), "
+                "protocols are chosen in the specified order")
+#endif
+
     /* Deprecated directives. */
     AP_INIT_RAW_ARGS("SSLLog", ap_set_deprecated, NULL, OR_ALL,
       "SSLLog directive is no longer supported - use ErrorLog."),
@@ -423,12 +448,44 @@
     return 1;
 }
 
+static int modssl_register_alpn(conn_rec *c,
+                               ssl_alpn_propose_protos advertisefn,
+                               ssl_alpn_proto_negotiated negotiatedfn)
+{
+#ifdef HAVE_TLS_ALPN
+    SSLConnRec *sslconn = myConnConfig(c);
+
+    if (!sslconn) {
+        return DECLINED;
+    }
+
+    if (!sslconn->alpn_proposefns) {
+        sslconn->alpn_proposefns =
+        apr_array_make(c->pool, 5, sizeof(ssl_alpn_propose_protos));
+        sslconn->alpn_negofns =
+        apr_array_make(c->pool, 5, sizeof(ssl_alpn_proto_negotiated));
+    }
+
+    if (advertisefn)
+        APR_ARRAY_PUSH(sslconn->alpn_proposefns, ssl_alpn_propose_protos) =
+            advertisefn;
+    if (negotiatedfn)
+        APR_ARRAY_PUSH(sslconn->alpn_negofns, ssl_alpn_proto_negotiated) =
+            negotiatedfn;
+
+    return OK;
+#else
+    return DECLINED;
+#endif
+}
+
 int ssl_init_ssl_connection(conn_rec *c, request_rec *r)
 {
     SSLSrvConfigRec *sc;
     SSL *ssl;
     SSLConnRec *sslconn = myConnConfig(c);
     char *vhost_md5;
+    int rc;
     modssl_ctx_t *mctx;
     server_rec *server;
 
@@ -585,6 +647,7 @@
 
     APR_REGISTER_OPTIONAL_FN(ssl_proxy_enable);
     APR_REGISTER_OPTIONAL_FN(ssl_engine_disable);
+    APR_REGISTER_OPTIONAL_FN(modssl_register_alpn);
 
     ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "ssl",
                               AUTHZ_PROVIDER_VERSION,
--- modules/ssl/mod_ssl.h
+++ modules/ssl/mod_ssl.h
@@ -63,5 +93,46 @@
 
 APR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec *));
 
+/** The alpn_propose_proto callback allows other modules to propose
+ * the name of the protocol that will be chosen during the
+ * Application-Layer Protocol Negotiation (ALPN) portion of the SSL handshake.
+ * The callback is given the connection and a list of NULL-terminated
+ * protocol strings as supported by the client.  If this client_protos is
+ * non-empty, it must pick its preferred protocol from that list. Otherwise
+ * it should add its supported protocols in order of precedence.
+ * The callback should not yet modify the connection or install any filters
+ * as its proposal(s) may be overridden by another callback or server
+ * configuration.
+ * It should return OK or, to prevent further processing of (other modules')
+ * callbacks, return DONE.
+ */
+typedef int (*ssl_alpn_propose_protos)(conn_rec *connection,
+                                    apr_array_header_t *client_protos,
+                                    apr_array_header_t *proposed_protos);
+
+/** The alpn_proto_negotiated callback allows other modules to discover
+ * the name of the protocol that was chosen during the Application-Layer
+ * Protocol Negotiation (ALPN) portion of the SSL handshake.
+ * The callback is given the connection, a
+ * non-NUL-terminated string containing the protocol name, and the
+ * length of the string; it should do something appropriate
+ * (i.e. insert or remove filters) and return OK. To prevent further
+ * processing of (other modules') callbacks, return DONE. */
+typedef int (*ssl_alpn_proto_negotiated)(conn_rec *connection,
+                                        const char *proto_name,
+                                        apr_size_t proto_name_len);
+
+/* An optional function which can be used to register a pair of callbacks
+ * for ALPN handling.
+ * This optional function should be invoked from a pre_connection hook
+ * which runs *after* mod_ssl.c's pre_connection hook.  The function returns
+ * OK if the callbacks are registered, or DECLINED otherwise (for example if
+ * mod_ssl does not support ALPN).
+ */
+APR_DECLARE_OPTIONAL_FN(int, modssl_register_alpn,
+                        (conn_rec *conn,
+                         ssl_alpn_propose_protos proposefn,
+                         ssl_alpn_proto_negotiated negotiatedfn));
+
 #endif /* __MOD_SSL_H__ */
 /** @} */
--- modules/ssl/ssl_engine_config.c
+++ modules/ssl/ssl_engine_config.c
@@ -159,6 +160,9 @@
     SSL_CONF_CTX_set_flags(mctx->ssl_ctx_config, SSL_CONF_FLAG_CERTIFICATE);
     mctx->ssl_ctx_param = apr_array_make(p, 5, sizeof(ssl_ctx_param_t));
 #endif
+#ifdef HAVE_TLS_ALPN
+    mctx->ssl_alpn_pref = apr_array_make(p, 5, sizeof(const char *));
+#endif
 }
 
 static void modssl_ctx_init_proxy(SSLSrvConfigRec *sc,
@@ -301,6 +307,9 @@
 #ifdef HAVE_SSL_CONF_CMD
     cfgMergeArray(ssl_ctx_param);
 #endif
+#ifdef HAVE_TLS_ALPN
+    cfgMergeArray(ssl_alpn_pref);
+#endif
 }
 
 static void modssl_ctx_cfg_merge_proxy(apr_pool_t *p,
@@ -1875,6 +1868,16 @@
 }
 #endif
 
+#ifdef HAVE_TLS_ALPN
+const char *ssl_cmd_SSLALPNPreference(cmd_parms *cmd, void *dcfg,
+                                      const char *protocol)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    APR_ARRAY_PUSH(sc->server->ssl_alpn_pref, const char *) = protocol;
+    return NULL;
+}
+#endif
+
 #ifdef HAVE_SRP
 
 const char *ssl_cmd_SSLSRPVerifierFile(cmd_parms *cmd, void *dcfg,
--- modules/ssl/ssl_engine_init.c
+++ modules/ssl/ssl_engine_init.c
@@ -623,6 +646,11 @@
     SSL_CTX_set_tmp_dh_callback(ctx,  ssl_callback_TmpDH);
 
     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
 }
 
 static apr_status_t ssl_init_ctx_verify(server_rec *s,
--- modules/ssl/ssl_engine_io.c
+++ modules/ssl/ssl_engine_io.c
@@ -28,6 +28,7 @@
                                   core keeps dumping.''
                                             -- Unknown    */
 #include "ssl_private.h"
+#include "mod_ssl.h"
 #include "apr_date.h"
 
 /*  _________________________________________________________________
@@ -297,6 +315,9 @@
     apr_pool_t *pool;
     char buffer[AP_IOBUFSIZE];
     ssl_filter_ctx_t *filter_ctx;
+#ifdef HAVE_TLS_ALPN
+    int alpn_finished;  /* 1 if ALPN has finished, 0 otherwise */
+#endif
 } bio_filter_in_ctx_t;
 
 /*
@@ -1412,6 +1485,37 @@
         APR_BRIGADE_INSERT_TAIL(bb, bucket);
     }
 
+#ifdef HAVE_TLS_ALPN
+    /* By this point, Application-Layer Protocol Negotiation (ALPN) should be
+     * completed (if our version of OpenSSL supports it). If we haven't 
already,
+     * find out which protocol was decided upon and inform other modules
+     * by calling alpn_proto_negotiated_hook.
+     */
+    if (!inctx->alpn_finished) {
+        SSLConnRec *sslconn = myConnConfig(f->c);
+        const unsigned char *next_proto = NULL;
+        unsigned next_proto_len = 0;
+        int n;
+
+        if (sslconn->alpn_negofns) {
+            SSL_get0_alpn_selected(inctx->ssl, &next_proto, &next_proto_len);
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, f->c,
+                          APLOGNO(02836) "SSL negotiated protocol: '%s'",
+                          (next_proto && next_proto_len)?
+                         apr_pstrmemdup(f->c->pool, (const char *)next_proto,
+                              next_proto_len) : "(null)");
+            for (n = 0; n < sslconn->alpn_negofns->nelts; n++) {
+                ssl_alpn_proto_negotiated fn =
+                APR_ARRAY_IDX(sslconn->alpn_negofns, n, 
ssl_alpn_proto_negotiated);
+
+                if (fn(f->c, (const char *)next_proto, next_proto_len) == DONE)
+                break;
+            }
+        }
+        inctx->alpn_finished = 1;
+    }
+#endif
+
     return APR_SUCCESS;
 }
 
@@ -1893,6 +1996,9 @@
     inctx->block = APR_BLOCK_READ;
     inctx->pool = c->pool;
     inctx->filter_ctx = filter_ctx;
+#ifdef HAVE_TLS_ALPN
+    inctx->alpn_finished = 0;
+#endif
 }
 
 /* The request_rec pointer is passed in here only to ensure that the
--- modules/ssl/ssl_engine_kernel.c
+++ modules/ssl/ssl_engine_kernel.c
@@ -29,6 +29,7 @@
                                   time I was too famous.''
                                             -- Unknown                */
 #include "ssl_private.h"
+#include "mod_ssl.h"
 #include "util_md5.h"
 
 static void ssl_configure_env(request_rec *r, SSLConnRec *sslconn);
@@ -2137,6 +2162,153 @@
 }
 #endif /* HAVE_TLS_SESSION_TICKETS */
 
+#ifdef HAVE_TLS_ALPN
+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;
+}
+
+/*
+ * Compare two 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;
+    size_t len;
+
+    /* 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(02837)
+                      "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(02838)
+                          "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;
+    }
+
+    proposed_protos = apr_array_make(c->pool, client_protos->nelts+1,
+                                     sizeof(char *));
+
+    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) {
+        /* Regardless of installed hooks, the http/1.1 protocol is always
+         * supported by us. Choose it if none other matches. */
+        if (ssl_array_index(client_protos, alpn_http1) < 0) {
+            ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02839)
+                          "none of the client ALPN protocols are supported");
+            return SSL_TLSEXT_ERR_ALERT_FATAL;
+        }
+        *out = (const unsigned char*)alpn_http1;
+        *outlen = (unsigned char)strlen(alpn_http1);
+        return SSL_TLSEXT_ERR_OK;
+    }
+
+    /* 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;
+        }
+    }
+
+    len = strlen((const char*)*out);
+    if (len > 255) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02840)
+                      "ALPN negotiated protocol name too long");
+        return SSL_TLSEXT_ERR_ALERT_FATAL;
+    }
+    *outlen = (unsigned char)len;
+
+    return SSL_TLSEXT_ERR_OK;
+}
+#endif
+
 #ifdef HAVE_SRP
 
 int ssl_callback_SRPServerParams(SSL *ssl, int *ad, void *arg)
--- modules/ssl/ssl_private.h
+++ modules/ssl/ssl_private.h
@@ -182,6 +182,11 @@
 #include <openssl/srp.h>
 #endif
 
+/* ALPN Protocol Negotiation */
+#if defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
+#define HAVE_TLS_ALPN
+#endif
+
 #endif /* !defined(OPENSSL_NO_TLSEXT) && defined(SSL_set_tlsext_host_name) */
 
 /* mod_ssl headers */
@@ -443,6 +438,12 @@
                      * connection */
     } reneg_state;
 
+#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
+
     server_rec *server;
 } SSLConnRec;
 
@@ -633,6 +633,10 @@
     SSL_CONF_CTX *ssl_ctx_config; /* Configuration context */
     apr_array_header_t *ssl_ctx_param; /* parameters to pass to SSL_CTX */
 #endif
+
+#ifdef HAVE_TLS_ALPN
+  apr_array_header_t *ssl_alpn_pref; /* protocol names in order of preference 
*/
+#endif
 } modssl_ctx_t;
 
 struct SSLSrvConfigRec {
@@ -763,6 +763,10 @@
 const char *ssl_cmd_SSLOpenSSLConfCmd(cmd_parms *cmd, void *dcfg, const char 
*arg1, const char *arg2);
 #endif
 
+#ifdef HAVE_TLS_ALPN
+const char *ssl_cmd_SSLALPNPreference(cmd_parms *cmd, void *dcfg, const char 
*protocol);
+#endif
+
 #ifdef HAVE_SRP
 const char *ssl_cmd_SSLSRPVerifierFile(cmd_parms *cmd, void *dcfg, const char 
*arg);
 const char *ssl_cmd_SSLSRPUnknownUserSeed(cmd_parms *cmd, void *dcfg, const 
char *arg);
@@ -815,6 +815,12 @@
                                        EVP_CIPHER_CTX *, HMAC_CTX *, int);
 #endif
 
+#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
+
 /**  Session Cache Support  */
 apr_status_t ssl_scache_init(server_rec *, apr_pool_t *);
 void         ssl_scache_status_register(apr_pool_t *p);




Reply via email to