BUG/MAJOR: quic: Crash with TLS_AES_128_CCM_SHA256 (libressl only)

At least 3.9.0 version of libressl TLS stack does not behave as others stacks like quictls which
make SSL_do_handshake() return an error when no cipher could be negotiated
in addition to emit a TLS alert(0x28). This is the case when TLS_AES_128_CCM_SHA256
is forced as TLS1.3 cipher from the client side. This make haproxy enter a code
path which leads to a crash as follows:

[Switching to Thread 0x7ffff76b9640 (LWP 23902)]
0x0000000000487627 in quic_tls_key_update (qc=qc@entry=0x7ffff00371f0) at src/quic_tls.c:910
910             struct quic_kp_trace kp_trace = {
(gdb) list
905     {
906             struct quic_tls_ctx *tls_ctx = &qc->ael->tls_ctx;
907             struct quic_tls_secrets *rx = &tls_ctx->rx;
908             struct quic_tls_secrets *tx = &tls_ctx->tx;
909             /* Used only for the traces */
910             struct quic_kp_trace kp_trace = {
911                     .rx_sec = rx->secret,
912                     .rx_seclen = rx->secretlen,
913                     .tx_sec = tx->secret,
914                     .tx_seclen = tx->secretlen,
(gdb) p qc
$1 = (struct quic_conn *) 0x7ffff00371f0
(gdb) p qc->ael
$2 = (struct quic_enc_level *) 0x0
(gdb) bt
 #0  0x0000000000487627 in quic_tls_key_update (qc=qc@entry=0x7ffff00371f0) at src/quic_tls.c:910
 #1  0x000000000049bca9 in qc_ssl_provide_quic_data (len=268, data=<optimized out>, ctx=0x7ffff0047f80, level=<optimized out>, ncbuf=<optimized out>) at src/quic_ssl.c:617
 #2  qc_ssl_provide_all_quic_data (qc=qc@entry=0x7ffff00371f0, ctx=0x7ffff0047f80) at src/quic_ssl.c:688
 #3  0x00000000004683a7 in quic_conn_io_cb (t=0x7ffff0047f30, context=0x7ffff00371f0, state=<optimized out>) at src/quic_conn.c:760
 #4  0x000000000063cd9c in run_tasks_from_lists (budgets=budgets@entry=0x7ffff76961f0) at src/task.c:596
 #5  0x000000000063d934 in process_runnable_tasks () at src/task.c:876
 #6  0x0000000000600508 in run_poll_loop () at src/haproxy.c:3073
 #7  0x0000000000600b67 in run_thread_poll_loop (data=<optimized out>) at src/haproxy.c:3287
 #8  0x00007ffff7f6ae45 in start_thread () from /lib64/libpthread.so.0
 #9  0x00007ffff78254af in clone () from /lib64/libc.so.6

When a TLS alert is emitted, haproxy calls quic_set_connection_close() which sets
QUIC_FL_CONN_IMMEDIATE_CLOSE connection flag. This is this flag which is tested
by this patch to make the handshake fail even if SSL_do_handshake() does not
return an error. This test is specific to libressl and never run with
others TLS stack.

Thank you to @lgv5 and @botovq for having reported this issue in GH #2569.

Must be backported as far as 2.6.
This commit is contained in:
Frederic Lecaille 2024-05-22 10:22:09 +02:00
parent 0e93549d2a
commit 169fc0b771

View File

@ -557,6 +557,17 @@ static int qc_ssl_provide_quic_data(struct ncbuf *ncbuf,
ERR_clear_error();
goto leave;
}
#if defined(LIBRESSL_VERSION_NUMBER)
else if (qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE) {
/* Some libressl versions emit TLS alerts without making the handshake
* (SSL_do_handshake()) fail. This is at least the case for
* libressl-3.9.0 when forcing the TLS cipher to TLS_AES_128_CCM_SHA256.
*/
TRACE_ERROR("SSL handshake error", QUIC_EV_CONN_IO_CB, qc, &state, &ssl_err);
HA_ATOMIC_INC(&qc->prx_counters->hdshk_fail);
goto leave;
}
#endif
#if defined(OPENSSL_IS_AWSLC)
/* As a server, if early data is accepted, SSL_do_handshake will