mirror of
https://github.com/apache/httpd.git
synced 2025-08-07 04:02:58 +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:
@@ -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">
|
||||||
|
@@ -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 */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -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
|
||||||
|
@@ -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) {
|
||||||
|
@@ -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
|
||||||
|
@@ -2210,14 +2210,30 @@ 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;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user