1
0
mirror of https://github.com/apache/httpd.git synced 2025-08-08 15:02:10 +03:00

mod_ssl: performing protocol switch directly after ALPN selection, mod_http2: connection hook inits network filters to force TLS handshake, reads input only if H2Direct explicitly enabled, changes H2Direct default to off even for cleartext connections

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1708107 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Stefan Eissing
2015-10-12 13:13:45 +00:00
parent d5c6229953
commit ebb34c0b07
6 changed files with 65 additions and 67 deletions

View File

@@ -56,7 +56,7 @@
<name>H2Direct</name> <name>H2Direct</name>
<description>H2 Direct Protocol Switch</description> <description>H2 Direct Protocol Switch</description>
<syntax>H2Direct on|off</syntax> <syntax>H2Direct on|off</syntax>
<default>H2Direct on (for non TLS)</default> <default>H2Direct off</default>
<contextlist> <contextlist>
<context>server config</context> <context>server config</context>
<context>virtual host</context> <context>virtual host</context>
@@ -64,16 +64,23 @@
<usage> <usage>
<p> <p>
This directive toggles the usage of the HTTP/2 Direct Mode. This This directive toggles the usage of the HTTP/2 Direct Mode. For https, this
should be used inside a should be used inside a
<directive module="core" type="section">VirtualHost</directive> <directive module="core" type="section">VirtualHost</directive>
section to enable direct HTTP/2 communication for that virtual host. section to enable direct HTTP/2 communication for that virtual host. For
http, this directive only works in server configurations.
</p>
<p>
Direct communication means that if the first bytes received by the Direct communication means that if the first bytes received by the
server on a connection match the HTTP/2 preamble, the HTTP/2 server on a connection match the HTTP/2 preamble, the HTTP/2
protocol is switched to immediately without further negotiation. protocol is switched to immediately without further negotiation.
This mode falls outside the RFC 7540 but has become widely implemented This mode falls outside the RFC 7540 but has become widely implemented
as it is very convenient for development and testing. on cleartext ports as it is very convenient for development and testing.
By default the direct HTTP/2 mode is enabled. </p>
<p>
Since this detection implies that the client will send data on
new connection immediately, direct HTTP/2 mode is disabled by
default.
</p> </p>
<example><title>Example</title> <example><title>Example</title>
<highlight language="config"> <highlight language="config">

View File

@@ -47,7 +47,7 @@ static h2_config defconf = {
NULL, /* no alt-svcs */ NULL, /* no alt-svcs */
-1, /* alt-svc max age */ -1, /* alt-svc max age */
0, /* serialize headers */ 0, /* serialize headers */
-1, /* h2 direct mode */ 0, /* h2 direct mode */
-1, /* # session extra files */ -1, /* # session extra files */
}; };

View File

@@ -135,7 +135,7 @@ int h2_h2_process_conn(conn_rec* c)
{ {
h2_ctx *ctx = h2_ctx_get(c); h2_ctx *ctx = h2_ctx_get(c);
h2_config *cfg = h2_config_get(c); h2_config *cfg = h2_config_get(c);
apr_bucket_brigade* temp; apr_bucket_brigade* temp = NULL;
int is_tls = h2_h2_is_tls(c); int is_tls = h2_h2_is_tls(c);
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, process_conn"); ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, process_conn");
@@ -150,11 +150,14 @@ int h2_h2_process_conn(conn_rec* c)
*/ */
if (!h2_ctx_protocol_get(c) if (!h2_ctx_protocol_get(c)
&& !strcmp(AP_PROTOCOL_HTTP1, ap_get_protocol(c))) { && !strcmp(AP_PROTOCOL_HTTP1, ap_get_protocol(c))) {
apr_status_t status; apr_status_t status = APR_SUCCESS;
temp = apr_brigade_create(c->pool, c->bucket_alloc); if (is_tls) {
status = ap_get_brigade(c->input_filters, temp, /* trigger the TLS handshake */
AP_MODE_SPECULATIVE, APR_BLOCK_READ, 24); temp = apr_brigade_create(c->pool, c->bucket_alloc);
status = ap_get_brigade(c->input_filters, temp,
AP_MODE_INIT, APR_BLOCK_READ, 0);
}
if (status == APR_SUCCESS) { if (status == APR_SUCCESS) {
if (h2_ctx_protocol_get(c) if (h2_ctx_protocol_get(c)
@@ -166,30 +169,43 @@ int h2_h2_process_conn(conn_rec* c)
* http/1.1. Check the actual bytes read for the H2 Magic * http/1.1. Check the actual bytes read for the H2 Magic
* Token, *if* H2Direct mode is enabled here. * Token, *if* H2Direct mode is enabled here.
*/ */
int direct_mode = h2_config_geti(cfg, H2_CONF_DIRECT); if (h2_config_geti(cfg, H2_CONF_DIRECT) > 0) {
if (direct_mode > 0 || (direct_mode < 0 && !is_tls)) {
char *s = NULL; char *s = NULL;
apr_size_t slen; apr_size_t slen;
apr_brigade_pflatten(temp, &s, &slen, c->pool); if (!temp) {
if ((slen >= 24) && !memcmp(H2_MAGIC_TOKEN, s, 24)) { temp = apr_brigade_create(c->pool, c->bucket_alloc);
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, }
"h2_h2, direct mode detected"); status = ap_get_brigade(c->input_filters, temp,
h2_ctx_protocol_set(ctx, is_tls? "h2" : "h2c"); AP_MODE_SPECULATIVE, APR_BLOCK_READ, 24);
if (status == APR_SUCCESS) {
apr_brigade_pflatten(temp, &s, &slen, c->pool);
if ((slen >= 24) && !memcmp(H2_MAGIC_TOKEN, s, 24)) {
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
"h2_h2, direct mode detected");
h2_ctx_protocol_set(ctx, is_tls? "h2" : "h2c");
}
else {
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
"h2_h2, not detected in %d bytes: %s",
(int)slen, s);
}
} }
else { else {
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c,
"h2_h2, not detected in %d bytes: %s", "h2_h2, error reading 24 bytes speculative");
(int)slen, s);
} }
} }
} }
} }
else { else {
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c, ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c,
"h2_h2, error reading 24 bytes speculative"); "h2_h2, failed to init connection");
}
if (temp) {
apr_brigade_destroy(temp);
} }
apr_brigade_destroy(temp);
} }
/* If "h2" was selected as protocol (by whatever mechanism), take over /* If "h2" was selected as protocol (by whatever mechanism), take over

View File

@@ -116,7 +116,7 @@ apr_status_t h2_task_input_read(h2_task_input *input,
} }
if (mode == AP_MODE_INIT) { if (mode == AP_MODE_INIT) {
return APR_SUCCESS; return ap_get_brigade(f->c->input_filters, bb, mode, block, readbytes);
} }
if (input->bb) { if (input->bb) {

View File

@@ -336,9 +336,6 @@ typedef struct {
apr_pool_t *pool; apr_pool_t *pool;
char buffer[AP_IOBUFSIZE]; char buffer[AP_IOBUFSIZE];
ssl_filter_ctx_t *filter_ctx; 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; } bio_filter_in_ctx_t;
/* /*
@@ -1493,41 +1490,6 @@ static apr_status_t ssl_io_filter_input(ap_filter_t *f,
APR_BRIGADE_INSERT_TAIL(bb, bucket); 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 running protocol_switch hook.
*/
if (!inctx->alpn_finished) {
SSLConnRec *sslconn = myConnConfig(f->c);
const unsigned char *next_proto = NULL;
unsigned next_proto_len = 0;
const char *protocol;
SSL_get0_alpn_selected(inctx->ssl, &next_proto, &next_proto_len);
if (next_proto && next_proto_len) {
protocol = apr_pstrmemdup(f->c->pool, (const char *)next_proto,
next_proto_len);
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, f->c,
APLOGNO(02836) "ALPN selected protocol: '%s'",
protocol);
if (strcmp(protocol, ap_get_protocol(f->c))) {
status = ap_switch_protocol(f->c, NULL, sslconn->server,
protocol);
if (status != APR_SUCCESS) {
ap_log_cerror(APLOG_MARK, APLOG_ERR, status, f->c,
APLOGNO(02908) "protocol switch to '%s' failed",
protocol);
return status;
}
}
}
inctx->alpn_finished = 1;
}
#endif
return APR_SUCCESS; return APR_SUCCESS;
} }
@@ -2008,9 +1970,6 @@ static void ssl_io_input_add_filter(ssl_filter_ctx_t *filter_ctx, conn_rec *c,
inctx->block = APR_BLOCK_READ; inctx->block = APR_BLOCK_READ;
inctx->pool = c->pool; inctx->pool = c->pool;
inctx->filter_ctx = filter_ctx; 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 /* The request_rec pointer is passed in here only to ensure that the

View File

@@ -2210,15 +2210,31 @@ int ssl_callback_alpn_select(SSL *ssl,
init_vhost(c, ssl); init_vhost(c, ssl);
proposed = ap_select_protocol(c, NULL, sslconn->server, client_protos); proposed = ap_select_protocol(c, NULL, sslconn->server, client_protos);
*out = (const unsigned char *)(proposed? proposed : ap_get_protocol(c)); if (!proposed) {
len = strlen((const char*)*out); proposed = ap_get_protocol(c);
}
len = strlen(proposed);
if (len > 255) { if (len > 255) {
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02840) ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02840)
"ALPN negotiated protocol name too long"); "ALPN negotiated protocol name too long");
return SSL_TLSEXT_ERR_ALERT_FATAL; return SSL_TLSEXT_ERR_ALERT_FATAL;
} }
*out = (const unsigned char *)proposed;
*outlen = (unsigned char)len; *outlen = (unsigned char)len;
if (strcmp(proposed, ap_get_protocol(c))) {
apr_status_t status;
status = ap_switch_protocol(c, NULL, sslconn->server, proposed);
if (status != APR_SUCCESS) {
ap_log_cerror(APLOG_MARK, APLOG_ERR, status, c,
APLOGNO(02908) "protocol switch to '%s' failed",
proposed);
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
}
return SSL_TLSEXT_ERR_OK; return SSL_TLSEXT_ERR_OK;
} }
#endif /* HAVE_TLS_ALPN */ #endif /* HAVE_TLS_ALPN */