BUG/MEDIUM: mux-h2: reject upgrade if no RFC8441 support
The RFC8441 was not respected by haproxy in regards with server support
for Extended CONNECT. The Extended CONNECT method was used to convert an
Upgrade header stream even if no SETTINGS_ENABLE_CONNECT_PROTOCOL was
received, which is forbidden by the RFC8441. In this case, the behavior
of the http/2 server is unspecified.
Fix this by flagging the connection on receiption of the RFC8441
settings SETTINGS_ENABLE_CONNECT_PROTOCOL. Extended CONNECT is thus only
be used if the flag is present. In the other case, the stream is
immediatly closed as there is no way to handle it in http/2. It results
in a http/1.1 502 or http/2 RESET_STREAM to the client side.
The protocol-upgrade regtest has been extended to test that haproxy does
not emit Extended CONNECT on servers without RFC8441 support.
It must be backported up to 2.4.
diff --git a/src/mux_h2.c b/src/mux_h2.c
index 0418b58..9037331 100644
--- a/src/mux_h2.c
+++ b/src/mux_h2.c
@@ -72,6 +72,8 @@
#define H2_CF_RCVD_SHUT 0x00020000 // a recv() attempt already failed on a shutdown
#define H2_CF_END_REACHED 0x00040000 // pending data too short with RCVD_SHUT present
+#define H2_CF_RCVD_RFC8441 0x00100000 // settings from RFC8441 has been received indicating support for Extended CONNECT
+
/* H2 connection state, in h2c->st0 */
enum h2_cs {
H2_CS_PREFACE, // init done, waiting for connection preface
@@ -2239,8 +2241,8 @@
}
break;
case H2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
- /* nothing to do here as this settings is automatically
- * transmits to the client */
+ if (arg == 1)
+ h2c->flags |= H2_CF_RCVD_RFC8441;
break;
}
}
@@ -5319,6 +5321,11 @@
do {
if (isteqi(iststop(connection_ist, ','),
ist("upgrade"))) {
+ if (!(h2c->flags & H2_CF_RCVD_RFC8441)) {
+ TRACE_STATE("reject upgrade because of no RFC8441 support", H2_EV_TX_FRAME|H2_EV_TX_HDR, h2c->conn, h2s);
+ goto fail;
+ }
+
TRACE_STATE("convert upgrade to extended connect method", H2_EV_TX_FRAME|H2_EV_TX_HDR, h2c->conn, h2s);
h2s->flags |= (H2_SF_BODY_TUNNEL|H2_SF_EXT_CONNECT_SENT);
sl->info.req.meth = HTTP_METH_CONNECT;