Files
for-gentoo/www-servers/apache/files/apache-2.4.12-alpn.patch
T
2016-03-22 15:53:26 +01:00

477 lines
16 KiB
Diff

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);