From 169fc0b771011da6e888ec8cb9f6cccc78a21e56 Mon Sep 17 00:00:00 2001 From: Frederic Lecaille Date: Wed, 22 May 2024 10:22:09 +0200 Subject: [PATCH] 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=, ctx=0x7ffff0047f80, level=, ncbuf=) 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=) 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=) 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. --- src/quic_ssl.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/quic_ssl.c b/src/quic_ssl.c index 51a96c2a3..66eb68eb2 100644 --- a/src/quic_ssl.c +++ b/src/quic_ssl.c @@ -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