Compare commits

...

10 Commits

Author SHA1 Message Date
openeuler-ci-bot
d6dca68099
!171 fix CVE-2025-32464
From: @xinghe_1 
Reviewed-by: @jiangheng12 
Signed-off-by: @jiangheng12
2025-04-29 07:48:27 +00:00
xh
3d1a3148ca fix CVE-2025-32464 2025-04-29 14:59:21 +08:00
openeuler-ci-bot
d5d68cf310
!161 sync some pathes from upstream
From: @yangl777 
Reviewed-by: @robertxw 
Signed-off-by: @robertxw
2025-03-20 03:53:06 +00:00
yangl777
25cd8bcf22 sync some pathes from upstream 2025-03-17 10:42:32 +00:00
openeuler-ci-bot
a384b803bc
!155 [sync] PR-152: Fix CVE-2024-53008
From: @openeuler-sync-bot 
Reviewed-by: @wang--ge 
Signed-off-by: @wang--ge
2024-12-10 07:13:07 +00:00
wk333
11d681b403 Fix CVE-2024-53008
(cherry picked from commit e053271e1181ee3ddd317185751d515fada3a626)
2024-12-10 14:18:36 +08:00
openeuler-ci-bot
a77f1e6fd2
!148 backport upstream patches
From: @xinghe_1 
Reviewed-by: @wang--ge 
Signed-off-by: @wang--ge
2024-11-21 07:51:19 +00:00
xh
5c2977b534 backport upstream patches 2024-11-21 03:34:42 +00:00
openeuler-ci-bot
4a562f7c34
!142 Fix CVE-2024-49214
From: @starlet-dx 
Reviewed-by: @wang--ge 
Signed-off-by: @wang--ge
2024-10-14 08:29:13 +00:00
starlet-dx
aec3b76ff8 Fix CVE-2024-49214 2024-10-14 15:39:51 +08:00
9 changed files with 944 additions and 1 deletions

458
CVE-2024-49214.patch Normal file
View File

@ -0,0 +1,458 @@
From fe5685af820ae62fe5b0d80b5ed7a2ffc41a036f Mon Sep 17 00:00:00 2001
From: Frederic Lecaille <flecaille@haproxy.com>
Date: Fri, 30 Aug 2024 15:38:54 +0200
Subject: [PATCH] BUG/MEDIUM: quic: always validate sender address on 0-RTT
It has been reported by Wedl Michael, a student at the University of Applied
Sciences St. Poelten, a potential vulnerability into haproxy as described below.
An attacker could have obtained a TLS session ticket after having established
a connection to an haproxy QUIC listener, using its real IP address. The
attacker has not even to send a application level request (HTTP3). Then
the attacker could open a 0-RTT session with a spoofed IP address
trusted by the QUIC listen to bypass IP allow/block list and send HTTP3 requests.
To mitigate this vulnerability, one decided to use a token which can be provided
to the client each time it successfully managed to connect to haproxy. These
tokens may be reused for future connections to validate the address/path of the
remote peer as this is done with the Retry token which is used for the current
connection, not the next one. Such tokens are transported by NEW_TOKEN frames
which was not used at this time by haproxy.
So, each time a client connect to an haproxy QUIC listener with 0-RTT
enabled, it is provided with such a token which can be reused for the
next 0-RTT session. If no such a token is presented by the client,
haproxy checks if the session is a 0-RTT one, so with early-data presented
by the client. Contrary to the Retry token, the decision to refuse the
connection is made only when the TLS stack has been provided with
enough early-data from the Initial ClientHello TLS message and when
these data have been accepted. Hopefully, this event arrives fast enough
to allow haproxy to kill the connection if some early-data have been accepted
without token presented by the client.
quic_build_post_handshake_frames() has been modified to build a NEW_TOKEN
frame with this newly implemented token to be transported inside.
quic_tls_derive_retry_token_secret() was renamed to quic_do_tls_derive_token_secre()
and modified to be reused and derive the secret for the new token implementation.
quic_token_validate() has been implemented to validate both the Retry and
the new token implemented by this patch. When this is a non-retry token
which could not be validated, the datagram received is marked as requiring
a Retry packet to be sent, and no connection is created.
When the Initial packet does not embed any non-retry token and if 0-RTT is enabled
the connection is marked with this new flag: QUIC_FL_CONN_NO_TOKEN_RCVD. As soon
as the TLS stack detects that some early-data have been provided and accepted by
the client, the connection is marked to be killed (QUIC_FL_CONN_TO_KILL) from
ha_quic_add_handshake_data(). This is done calling qc_ssl_eary_data_accepted()
new function. The secret TLS handshake is interrupted as soon as possible returnin
0 from ha_quic_add_handshake_data(). The connection is also marked as
requiring a Retry packet to be sent (QUIC_FL_CONN_SEND_RETRY) from
ha_quic_add_handshake_data(). The the handshake I/O handler (quic_conn_io_cb())
knows how to behave: kill the connection after having sent a Retry packet.
About TLS stack compatibility, this patch is supported by aws-lc. It is
disabled for wolfssl which does not support 0-RTT at this time thanks
to HAVE_SSL_0RTT_QUIC.
This patch depends on these commits:
MINOR: quic: Add trace for QUIC_EV_CONN_IO_CB event.
MINOR: quic: Implement qc_ssl_eary_data_accepted().
MINOR: quic: Modify NEW_TOKEN frame structure (qf_new_token struct)
BUG/MINOR: quic: Missing incrementation in NEW_TOKEN frame builder
MINOR: quic: Token for future connections implementation.
MINOR: quic: Implement quic_tls_derive_token_secret().
MINOR: tools: Implement ipaddrcpy().
Must be backported as far as 2.6.
(cherry picked from commit f627b9272bd8ffca6f2f898bfafc6bf0b84b7d46)
[fl: Add ->flags to quic_dgram struct (would arrive with quic_initial feature).
Add QUIC_DGRAM_FL_ quic_dgram flags (would arrive with quic_initial feature).
Modify quic_rx_pkt_retrieve_conn() to fix a compilation issue and correctly
handle the "if (pkt->token_len) {}" else block to do so with quic_initial
feature]
Signed-off-by: Frederic Lecaille <flecaille@haproxy.com>
(cherry picked from commit e875aa59a9216d42639b802b5008afc733e4c940)
[wt: move QUIC_CONN_FL_* upper in quic_conn-t.h; ctx adj in quic_dgram;
include quic_cid-t for struct quic_cid in quic_rx-t.h]
Signed-off-by: Willy Tarreau <w@1wt.eu>
---
include/haproxy/quic_conn-t.h | 3 ++
include/haproxy/quic_rx-t.h | 2 ++
include/haproxy/quic_sock-t.h | 5 +++
src/quic_conn.c | 65 ++++++++++++++++++++++++++++++---
src/quic_retry.c | 8 +----
src/quic_rx.c | 80 +++++++++++++++++++++++++++++++++++------
src/quic_sock.c | 2 ++
src/quic_ssl.c | 20 ++++++++++-
8 files changed, 161 insertions(+), 24 deletions(-)
diff --git a/include/haproxy/quic_conn-t.h b/include/haproxy/quic_conn-t.h
index a126e04..382454c 100644
--- a/include/haproxy/quic_conn-t.h
+++ b/include/haproxy/quic_conn-t.h
@@ -291,6 +291,9 @@ struct quic_conn_cntrs {
#define QUIC_FL_CONN_IPKTNS_DCD (1U << 15) /* Initial packet number space discarded */
#define QUIC_FL_CONN_HPKTNS_DCD (1U << 16) /* Handshake packet number space discarded */
#define QUIC_FL_CONN_PEER_VALIDATED_ADDR (1U << 17) /* Peer address is considered as validated for this connection. */
+#define QUIC_FL_CONN_NO_TOKEN_RCVD (1U << 18) /* Client dit not send any token */
+#define QUIC_FL_CONN_SEND_RETRY (1U << 19) /* A send retry packet must be sent */
+/* gap here */
#define QUIC_FL_CONN_TO_KILL (1U << 24) /* Unusable connection, to be killed */
#define QUIC_FL_CONN_TX_TP_RECEIVED (1U << 25) /* Peer transport parameters have been received (used for the transmitting part) */
#define QUIC_FL_CONN_FINALIZED (1U << 26) /* QUIC connection finalized (functional, ready to send/receive) */
diff --git a/include/haproxy/quic_rx-t.h b/include/haproxy/quic_rx-t.h
index 9ef8e7a..e77755b 100644
--- a/include/haproxy/quic_rx-t.h
+++ b/include/haproxy/quic_rx-t.h
@@ -1,6 +1,8 @@
#ifndef _HAPROXY_RX_T_H
#define _HAPROXY_RX_T_H
+#include <haproxy/quic_cid-t.h>
+
extern struct pool_head *pool_head_quic_conn_rxbuf;
extern struct pool_head *pool_head_quic_dgram;
extern struct pool_head *pool_head_quic_rx_packet;
diff --git a/include/haproxy/quic_sock-t.h b/include/haproxy/quic_sock-t.h
index 67a5749..83ab32f 100644
--- a/include/haproxy/quic_sock-t.h
+++ b/include/haproxy/quic_sock-t.h
@@ -25,6 +25,9 @@ struct quic_receiver_buf {
struct mt_list rxbuf_el; /* list element into receiver.rxbuf_list. */
};
+#define QUIC_DGRAM_FL_REJECT 0x00000001
+#define QUIC_DGRAM_FL_SEND_RETRY 0x00000002
+
/* QUIC datagram */
struct quic_dgram {
void *owner;
@@ -38,6 +41,8 @@ struct quic_dgram {
struct list recv_list; /* elemt to quic_receiver_buf <dgram_list>. */
struct mt_list handler_list; /* elem to quic_dghdlr <dgrams>. */
+
+ int flags; /* QUIC_DGRAM_FL_* values */
};
/* QUIC datagram handler */
diff --git a/src/quic_conn.c b/src/quic_conn.c
index cb56fbe..d9808d2 100644
--- a/src/quic_conn.c
+++ b/src/quic_conn.c
@@ -56,6 +56,7 @@
#include <haproxy/quic_sock.h>
#include <haproxy/quic_stats.h>
#include <haproxy/quic_stream.h>
+#include <haproxy/quic_token.h>
#include <haproxy/quic_tp.h>
#include <haproxy/quic_trace.h>
#include <haproxy/quic_tx.h>
@@ -478,6 +479,30 @@ int quic_build_post_handshake_frames(struct quic_conn *qc)
}
LIST_APPEND(&frm_list, &frm->list);
+
+#ifdef HAVE_SSL_0RTT_QUIC
+ if (qc->li->bind_conf->ssl_conf.early_data) {
+ size_t new_token_frm_len;
+
+ frm = qc_frm_alloc(QUIC_FT_NEW_TOKEN);
+ if (!frm) {
+ TRACE_ERROR("frame allocation error", QUIC_EV_CONN_IO_CB, qc);
+ goto leave;
+ }
+
+ new_token_frm_len =
+ quic_generate_token(frm->new_token.data,
+ sizeof(frm->new_token.data), &qc->peer_addr);
+ if (!new_token_frm_len) {
+ TRACE_ERROR("token generation failed", QUIC_EV_CONN_IO_CB, qc);
+ goto leave;
+ }
+
+ BUG_ON(new_token_frm_len != sizeof(frm->new_token.data));
+ frm->new_token.len = new_token_frm_len;
+ LIST_APPEND(&frm_list, &frm->list);
+ }
+#endif
}
/* Initialize <max> connection IDs minus one: there is
@@ -759,6 +784,11 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state)
qc_ssl_provide_all_quic_data(qc, qc->xprt_ctx);
}
+ if (qc->flags & QUIC_FL_CONN_TO_KILL) {
+ TRACE_DEVEL("connection to be killed", QUIC_EV_CONN_PHPKTS, qc);
+ goto out;
+ }
+
/* Retranmissions */
if (qc->flags & QUIC_FL_CONN_RETRANS_NEEDED) {
TRACE_DEVEL("retransmission needed", QUIC_EV_CONN_PHPKTS, qc);
@@ -872,7 +902,25 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state)
quic_nictx_free(qc);
}
- if ((qc->flags & QUIC_FL_CONN_CLOSING) && qc->mux_state != QC_MUX_READY) {
+ if (qc->flags & QUIC_FL_CONN_SEND_RETRY) {
+ struct quic_counters *prx_counters;
+ struct proxy *prx = qc->li->bind_conf->frontend;
+ struct quic_rx_packet pkt = {
+ .scid = qc->dcid,
+ .dcid = qc->odcid,
+ };
+
+ prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe, &quic_stats_module);
+ if (send_retry(qc->li->rx.fd, &qc->peer_addr, &pkt, qc->original_version)) {
+ TRACE_ERROR("Error during Retry generation",
+ QUIC_EV_CONN_LPKT, NULL, NULL, NULL, qc->original_version);
+ }
+ else
+ HA_ATOMIC_INC(&prx_counters->retry_sent);
+ }
+
+ if ((qc->flags & (QUIC_FL_CONN_CLOSING|QUIC_FL_CONN_TO_KILL)) &&
+ qc->mux_state != QC_MUX_READY) {
quic_conn_release(qc);
qc = NULL;
}
@@ -979,11 +1027,15 @@ struct task *qc_process_timer(struct task *task, void *ctx, unsigned int state)
* for QUIC servers (or haproxy listeners).
* <dcid> is the destination connection ID, <scid> is the source connection ID.
* This latter <scid> CID as the same value on the wire as the one for <conn_id>
- * which is the first CID of this connection but a different internal representation used to build
+ * which is the first CID of this connection but a different internal
+ * representation used to build
* NEW_CONNECTION_ID frames. This is the responsibility of the caller to insert
* <conn_id> in the CIDs tree for this connection (qc->cids).
- * <token> is the token found to be used for this connection with <token_len> as
- * length. Endpoints addresses are specified via <local_addr> and <peer_addr>.
+ * <token> is a boolean denoting if a token was received for this connection
+ * from an Initial packet.
+ * <token_odcid> is the original destination connection ID which was embedded
+ * into the Retry token sent to the client before instantiated this connection.
+ * Endpoints addresses are specified via <local_addr> and <peer_addr>.
* Returns the connection if succeeded, NULL if not.
*/
struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
@@ -1090,6 +1142,9 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
qc->prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe,
&quic_stats_module);
qc->flags = QUIC_FL_CONN_LISTENER;
+ /* Mark this connection as having not received any token when 0-RTT is enabled. */
+ if (l->bind_conf->ssl_conf.early_data && !token)
+ qc->flags |= QUIC_FL_CONN_NO_TOKEN_RCVD;
qc->state = QUIC_HS_ST_SERVER_INITIAL;
/* Copy the client original DCID. */
qc->odcid = *dcid;
@@ -1112,7 +1167,7 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
/* If connection is instantiated due to an INITIAL packet with an
* already checked token, consider the peer address as validated.
*/
- if (token_odcid->len) {
+ if (token) {
TRACE_STATE("validate peer address due to initial token",
QUIC_EV_CONN_INIT, qc);
qc->flags |= QUIC_FL_CONN_PEER_VALIDATED_ADDR;
diff --git a/src/quic_retry.c b/src/quic_retry.c
index 2d6ea31..78ef88a 100644
--- a/src/quic_retry.c
+++ b/src/quic_retry.c
@@ -258,17 +258,11 @@ int quic_retry_token_check(struct quic_rx_packet *pkt,
TRACE_ENTER(QUIC_EV_CONN_LPKT, qc);
/* The caller must ensure this. */
- BUG_ON(!pkt->token_len);
+ BUG_ON(!pkt->token_len || *pkt->token != QUIC_TOKEN_FMT_RETRY);
prx = l->bind_conf->frontend;
prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe, &quic_stats_module);
- if (*pkt->token != QUIC_TOKEN_FMT_RETRY) {
- /* TODO: New token check */
- TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT, qc, NULL, NULL, pkt->version);
- goto leave;
- }
-
if (sizeof buf < tokenlen) {
TRACE_ERROR("too short buffer", QUIC_EV_CONN_LPKT, qc);
goto err;
diff --git a/src/quic_rx.c b/src/quic_rx.c
index 7bc5844..81eaa69 100644
--- a/src/quic_rx.c
+++ b/src/quic_rx.c
@@ -26,6 +26,7 @@
#include <haproxy/quic_stream.h>
#include <haproxy/quic_ssl.h>
#include <haproxy/quic_tls.h>
+#include <haproxy/quic_token.h>
#include <haproxy/quic_trace.h>
#include <haproxy/quic_tx.h>
#include <haproxy/ssl_sock.h>
@@ -1587,6 +1588,47 @@ static inline int quic_padding_check(const unsigned char *pos,
return pos == end;
}
+/* Validate the token, retry or not (provided by NEW_TOKEN) parsed into
+ * <pkt> RX packet from <dgram> datagram.
+ * Return 1 if succeded, 0 if not.
+ */
+static inline int quic_token_validate(struct quic_rx_packet *pkt,
+ struct quic_dgram *dgram,
+ struct listener *l, struct quic_conn *qc,
+ struct quic_cid *odcid)
+{
+ int ret = 0;
+
+ TRACE_ENTER(QUIC_EV_CONN_LPKT, qc);
+
+ switch (*pkt->token) {
+ case QUIC_TOKEN_FMT_RETRY:
+ ret = quic_retry_token_check(pkt, dgram, l, qc, odcid);
+ break;
+ case QUIC_TOKEN_FMT_NEW:
+ ret = quic_token_check(pkt, dgram, qc);
+ if (!ret) {
+ /* Fallback to a retry token in case of any error. */
+ dgram->flags |= QUIC_DGRAM_FL_SEND_RETRY;
+ }
+ break;
+ default:
+ TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT, qc, NULL, NULL, pkt->version);
+ break;
+ }
+
+ if (!ret)
+ goto err;
+
+ ret = 1;
+ leave:
+ TRACE_LEAVE(QUIC_EV_CONN_LPKT, qc);
+ return ret;
+ err:
+ TRACE_DEVEL("leaving in error", QUIC_EV_CONN_LPKT, qc);
+ goto leave;
+}
+
/* Find the associated connection to the packet <pkt> or create a new one if
* this is an Initial packet. <dgram> is the datagram containing the packet and
* <l> is the listener instance on which it was received.
@@ -1645,22 +1687,38 @@ static struct quic_conn *quic_rx_pkt_retrieve_conn(struct quic_rx_packet *pkt,
}
if (pkt->token_len) {
- /* Validate the token only when connection is unknown. */
- if (!quic_retry_token_check(pkt, dgram, l, qc, &token_odcid))
+ TRACE_PROTO("Initial with token", QUIC_EV_CONN_LPKT, NULL, NULL, NULL, pkt->version);
+ /* Validate the token, retry or not only when connection is unknown. */
+ if (!quic_token_validate(pkt, dgram, l, qc, &token_odcid)) {
+ if (dgram->flags & QUIC_DGRAM_FL_SEND_RETRY) {
+ if (send_retry(l->rx.fd, &dgram->saddr, pkt, pkt->version)) {
+ TRACE_ERROR("Error during Retry generation",
+ QUIC_EV_CONN_LPKT, NULL, NULL, NULL, pkt->version);
+ }
+ else
+ HA_ATOMIC_INC(&prx_counters->retry_sent);
+
+ goto out;
+ }
+
goto err;
+ }
}
- else if (!(l->bind_conf->options & BC_O_QUIC_FORCE_RETRY) &&
- HA_ATOMIC_LOAD(&prx_counters->half_open_conn) >= global.tune.quic_retry_threshold) {
- TRACE_PROTO("Initial without token, sending retry",
- QUIC_EV_CONN_LPKT, NULL, NULL, NULL, pkt->version);
- if (send_retry(l->rx.fd, &dgram->saddr, pkt, pkt->version)) {
- TRACE_ERROR("Error during Retry generation",
+ else {
+ TRACE_PROTO("Initial without token", QUIC_EV_CONN_LPKT, NULL, NULL, NULL, pkt->version);
+ if (!(l->bind_conf->options & BC_O_QUIC_FORCE_RETRY) &&
+ HA_ATOMIC_LOAD(&prx_counters->half_open_conn) >= global.tune.quic_retry_threshold) {
+ TRACE_PROTO("Initial without token, sending retry",
QUIC_EV_CONN_LPKT, NULL, NULL, NULL, pkt->version);
+ if (send_retry(l->rx.fd, &dgram->saddr, pkt, pkt->version)) {
+ TRACE_ERROR("Error during Retry generation",
+ QUIC_EV_CONN_LPKT, NULL, NULL, NULL, pkt->version);
+ goto out;
+ }
+
+ HA_ATOMIC_INC(&prx_counters->retry_sent);
goto out;
}
-
- HA_ATOMIC_INC(&prx_counters->retry_sent);
- goto out;
}
/* RFC 9000 7.2. Negotiating Connection IDs:
diff --git a/src/quic_sock.c b/src/quic_sock.c
index 7a18bac..6713bdb 100644
--- a/src/quic_sock.c
+++ b/src/quic_sock.c
@@ -292,6 +292,7 @@ static int quic_lstnr_dgram_dispatch(unsigned char *pos, size_t len, void *owner
dgram->saddr = *saddr;
dgram->daddr = *daddr;
dgram->qc = NULL;
+ dgram->flags = 0;
/* Attached datagram to its quic_receiver_buf and quic_dghdlrs. */
LIST_APPEND(dgrams, &dgram->recv_list);
@@ -778,6 +779,7 @@ int qc_rcv_buf(struct quic_conn *qc)
new_dgram->saddr = saddr;
new_dgram->daddr = daddr;
new_dgram->qc = NULL; /* set later via quic_dgram_parse() */
+ new_dgram->flags = 0;
TRACE_DEVEL("read datagram", QUIC_EV_CONN_RCV, qc, new_dgram);
diff --git a/src/quic_ssl.c b/src/quic_ssl.c
index 73bf8dc..b48494e 100644
--- a/src/quic_ssl.c
+++ b/src/quic_ssl.c
@@ -354,6 +354,23 @@ static int ha_quic_add_handshake_data(SSL *ssl, enum ssl_encryption_level_t leve
TRACE_ENTER(QUIC_EV_CONN_ADDDATA, qc);
+ TRACE_PROTO("ha_quic_add_handshake_data() called", QUIC_EV_CONN_IO_CB, qc, NULL, ssl);
+
+#ifdef HAVE_SSL_0RTT_QUIC
+ /* Detect asap if some 0-RTT data were accepted for this connection.
+ * If this is the case and no token was provided, interrupt the useless
+ * secrets derivations. A Retry packet must be sent, and this connection
+ * must be killed.
+ * Note that QUIC_FL_CONN_NO_TOKEN_RCVD is possibly set only for when 0-RTT is
+ * enabled for the connection.
+ */
+ if ((qc->flags & QUIC_FL_CONN_NO_TOKEN_RCVD) && qc_ssl_eary_data_accepted(ssl)) {
+ TRACE_PROTO("connection to be killed", QUIC_EV_CONN_ADDDATA, qc);
+ qc->flags |= QUIC_FL_CONN_TO_KILL|QUIC_FL_CONN_SEND_RETRY;
+ goto leave;
+ }
+#endif
+
if (qc->flags & QUIC_FL_CONN_TO_KILL) {
TRACE_PROTO("connection to be killed", QUIC_EV_CONN_ADDDATA, qc);
goto out;
@@ -528,9 +545,10 @@ int qc_ssl_provide_quic_data(struct ncbuf *ncbuf,
state = qc->state;
if (state < QUIC_HS_ST_COMPLETE) {
ssl_err = SSL_do_handshake(ctx->ssl);
+ TRACE_PROTO("SSL_do_handshake() called", QUIC_EV_CONN_IO_CB, qc, NULL, ctx->ssl);
if (qc->flags & QUIC_FL_CONN_TO_KILL) {
- TRACE_DEVEL("connection to be killed", QUIC_EV_CONN_IO_CB, qc);
+ TRACE_DEVEL("connection to be killed", QUIC_EV_CONN_IO_CB, qc, &state, ctx->ssl);
goto leave;
}
--
1.7.10.4

77
CVE-2024-53008-1.patch Normal file
View File

@ -0,0 +1,77 @@
From 87fefebfbe3df218103502046a0871b235a48087 Mon Sep 17 00:00:00 2001
From: Amaury Denoyelle <adenoyelle@haproxy.com>
Date: Fri, 28 Jun 2024 10:43:19 +0200
Subject: [PATCH] BUG/MEDIUM: h3: ensure the ":method" pseudo header is totally
valid
Origin: https://github.com/haproxy/haproxy/commit/87fefebfbe3df218103502046a0871b235a48087
Ensure pseudo-header method is only constitued of valid characters
according to RFC 9110. If an invalid value is found, the request is
rejected and stream is resetted.
Previously only characters forbidden in headers were rejected (NUL/CR/LF),
but this is insufficient for :method, where some other forbidden chars
might be used to trick a non-compliant backend server into seeing a
different path from the one seen by haproxy. Note that header injection
is not possible though.
This must be backported up to 2.6.
Many thanks to Yuki Mogi of FFRI Security Inc for the detailed report
that allowed to quicky spot, confirm and fix the problem.
(cherry picked from commit 789d4abd7328f0a745d67698e89bbb888d4d9b2c)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 47d13c68cf198467a94e85a1caa44484a1e2e75c)
[cf: adapted]
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
---
include/haproxy/http.h | 15 +++++++++++++++
src/h3.c | 8 ++++++++
2 files changed, 23 insertions(+)
diff --git a/include/haproxy/http.h b/include/haproxy/http.h
index 299264051d28e..a297fa59b444a 100644
--- a/include/haproxy/http.h
+++ b/include/haproxy/http.h
@@ -192,6 +192,21 @@ static inline int http_header_has_forbidden_char(const struct ist ist, const cha
return 0;
}
+/* Check that method only contains token as required.
+ * See RFC 9110 9. Methods
+ */
+static inline int http_method_has_forbidden_char(const struct ist ist)
+{
+ const char *start = istptr(ist);
+
+ do {
+ if (!HTTP_IS_TOKEN(*start))
+ return 1;
+ start++;
+ } while (start < istend(ist));
+ return 0;
+}
+
/* Looks into <ist> for forbidden characters for :path values (0x00..0x1F,
* 0x20, 0x23), starting at pointer <start> which must be within <ist>.
* Returns non-zero if such a character is found, 0 otherwise. When run on
diff --git a/src/h3.c b/src/h3.c
index 9e415b3b56303..4e21f6b92f535 100644
--- a/src/h3.c
+++ b/src/h3.c
@@ -625,6 +625,14 @@ static ssize_t h3_headers_to_htx(struct qcs *qcs, const struct buffer *buf,
len = -1;
goto out;
}
+
+ if (!istlen(list[hdr_idx].v) || http_method_has_forbidden_char(list[hdr_idx].v)) {
+ TRACE_ERROR("invalid method pseudo-header", H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, qcs);
+ h3s->err = H3_MESSAGE_ERROR;
+ len = -1;
+ goto out;
+ }
+
meth = list[hdr_idx].v;
}
else if (isteq(list[hdr_idx].n, ist(":path"))) {

45
CVE-2024-53008-2.patch Normal file
View File

@ -0,0 +1,45 @@
From 6748a47819c263d4631187b6f121b5344ab50d57 Mon Sep 17 00:00:00 2001
From: Amaury Denoyelle <adenoyelle@haproxy.com>
Date: Fri, 28 Jun 2024 10:50:19 +0200
Subject: [PATCH] BUG/MEDIUM: h3: ensure the ":scheme" pseudo header is totally
valid
Origin: https://github.com/haproxy/haproxy/commit/6748a47819c263d4631187b6f121b5344ab50d57
Ensure pseudo-header scheme is only constitued of valid characters
according to RFC 9110. If an invalid value is found, the request is
rejected and stream is resetted.
It's the same as for previous commit "BUG/MEDIUM: h3: ensure the
":method" pseudo header is totally valid" except that this time it
applies to the ":scheme" pseudo header.
This must be backported up to 2.6.
(cherry picked from commit a3bed52d1f84ba36af66be4317a5f746d498bdf4)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 5ddc4004cb0c3c4ea4f4596577c85f004678e9c0)
[cf: adapted]
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
---
src/h3.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/src/h3.c b/src/h3.c
index 4e21f6b92f535..1984f984f7daf 100644
--- a/src/h3.c
+++ b/src/h3.c
@@ -666,6 +666,14 @@ static ssize_t h3_headers_to_htx(struct qcs *qcs, const struct buffer *buf,
len = -1;
goto out;
}
+
+ if (!http_validate_scheme(list[hdr_idx].v)) {
+ TRACE_ERROR("invalid scheme pseudo-header", H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, qcs);
+ h3s->err = H3_MESSAGE_ERROR;
+ len = -1;
+ goto out;
+ }
+
scheme = list[hdr_idx].v;
}
else if (isteq(list[hdr_idx].n, ist(":authority"))) {

View File

@ -0,0 +1,87 @@
From 4fb445fe5769172354d08f4a726f99e9815494c1 Mon Sep 17 00:00:00 2001
From: Olivier Houchard <ohouchard@haproxy.com>
Date: Mon, 23 Dec 2024 14:17:25 +0000
Subject: [PATCH] BUG/MEDIUM: queue: Make process_srv_queue return the number
of streams
Make process_srv_queue() return the number of streams unqueued, as
pendconn_grab_from_px() did, as that number is used by
srv_update_status() to generate logs.
This should be backported up to 2.6 with
111ea83ed4e13ac3ab028ed5e95201a1b4aa82b8
(cherry picked from commit 5b8899b6ccc7dab3a54a51dcb8ba1512bd0c886c)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 70588a16903002709cf3c84255ad8ded73f8e584)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 365378bfdf283650ce1ac152348ca59b6d4c32c1)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
Conflict:NA
Reference:https://git.haproxy.org/?p=haproxy-2.9.git;a=patch;h=4fb445fe5769172354d08f4a726f99e9815494c1
---
include/haproxy/queue.h | 2 +-
src/queue.c | 3 ++-
src/server.c | 4 ++--
3 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/include/haproxy/queue.h b/include/haproxy/queue.h
index e4201fb..4896f71 100644
--- a/include/haproxy/queue.h
+++ b/include/haproxy/queue.h
@@ -34,7 +34,7 @@ extern struct pool_head *pool_head_pendconn;
struct pendconn *pendconn_add(struct stream *strm);
int pendconn_dequeue(struct stream *strm);
-void process_srv_queue(struct server *s);
+int process_srv_queue(struct server *s);
unsigned int srv_dynamic_maxconn(const struct server *s);
int pendconn_redistribute(struct server *s);
int pendconn_grab_from_px(struct server *s);
diff --git a/src/queue.c b/src/queue.c
index 7555e2d..b93edf2 100644
--- a/src/queue.c
+++ b/src/queue.c
@@ -354,7 +354,7 @@ static int pendconn_process_next_strm(struct server *srv, struct proxy *px, int
/* Manages a server's connection queue. This function will try to dequeue as
* many pending streams as possible, and wake them up.
*/
-void process_srv_queue(struct server *s)
+int process_srv_queue(struct server *s)
{
struct server *ref = s->track ? s->track : s;
struct proxy *p = s->proxy;
@@ -413,6 +413,7 @@ void process_srv_queue(struct server *s)
if (p->lbprm.server_take_conn)
p->lbprm.server_take_conn(s);
}
+ return done;
}
/* Adds the stream <strm> to the pending connection queue of server <strm>->srv
diff --git a/src/server.c b/src/server.c
index 512fecd..cc2311a 100644
--- a/src/server.c
+++ b/src/server.c
@@ -6012,7 +6012,7 @@ static int _srv_update_status_op(struct server *s, enum srv_op_st_chg_cause caus
/* check if we can handle some connections queued.
* We will take as many as we can handle.
*/
- process_srv_queue(s);
+ xferred = process_srv_queue(s);
tmptrash = alloc_trash_chunk();
if (tmptrash) {
@@ -6198,7 +6198,7 @@ static int _srv_update_status_adm(struct server *s, enum srv_adm_st_chg_cause ca
/* check if we can handle some connections queued.
* We will take as many as we can handle.
*/
- process_srv_queue(s);
+ xferred = process_srv_queue(s);
}
else if (s->next_admin & SRV_ADMF_MAINT) {
/* remaining in maintenance mode, let's inform precisely about the
--
1.7.10.4

View File

@ -0,0 +1,89 @@
From e87aeeccfce15b27fb349c4a1f966c678d246417 Mon Sep 17 00:00:00 2001
From: Olivier Houchard <ohouchard@haproxy.com>
Date: Tue, 17 Dec 2024 15:39:21 +0100
Subject: [PATCH] BUG/MEDIUM: queues: Do not use pendconn_grab_from_px().
pendconn_grab_from_px() was called when a server was brought back up, to
get some streams waiting in the proxy's queue and get them to run on the
newly available server. It is very similar to process_srv_queue(),
except it only goes through the proxy's queue, which can be a problem,
because there is a small race condition that could lead us to add more
streams to the server queue just as it's going down. If that happens,
the server would just be ignored when back up by new streams, as its
queue is not empty, and it would never try to process its queue.
The other problem with pendconn_grab_from_px() is that it is very
liberal with how it dequeues streams, and it is not very good at
enforcing maxconn, it could lead to having 3*maxconn connections.
For both those reasons, just get rid of pendconn_grab_from_px(), and
just use process_srv_queue().
Both problems are easy to reproduce, especially on a 64 threads machine,
set a maxconn to 100, inject in H2 with 1000 concurrent connections
containing up to 100 streams each, and after a few seconds/minutes the
max number of concurrent output streams will be much higher than
maxconn, and eventually the server will stop processing connections.
It may be related to github issue #2744. Note that it doesn't totally
fix the problem, we can occasionally see a few more connections than
maxconn, but the max that have been observed is 4 more connections, we
no longer get multiple times maxconn.
have more outgoing connections than maxconn,
This should be backported up to 2.6.
(cherry picked from commit 111ea83ed4e13ac3ab028ed5e95201a1b4aa82b8)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit ab4ff1b7a6c7685f28fbdea01b38caf7e816fddf)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit b495692898072d6a843d36d4e66aae42e88a7c95)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
Conflict:NA
Reference:https://git.haproxy.org/?p=haproxy-2.9.git;a=patch;h=e87aeeccfce15b27fb349c4a1f966c678d246417
---
src/server.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/server.c b/src/server.c
index 311b495..512fecd 100644
--- a/src/server.c
+++ b/src/server.c
@@ -5305,7 +5305,7 @@ static struct task *server_warmup(struct task *t, void *context, unsigned int st
server_recalc_eweight(s, 1);
/* probably that we can refill this server with a bit more connections */
- pendconn_grab_from_px(s);
+ process_srv_queue(s);
HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
@@ -6009,10 +6009,10 @@ static int _srv_update_status_op(struct server *s, enum srv_op_st_chg_cause caus
!(s->flags & SRV_F_BACKUP) && s->next_eweight)
srv_shutdown_backup_streams(s->proxy, SF_ERR_UP);
- /* check if we can handle some connections queued at the proxy. We
- * will take as many as we can handle.
+ /* check if we can handle some connections queued.
+ * We will take as many as we can handle.
*/
- xferred = pendconn_grab_from_px(s);
+ process_srv_queue(s);
tmptrash = alloc_trash_chunk();
if (tmptrash) {
@@ -6195,10 +6195,10 @@ static int _srv_update_status_adm(struct server *s, enum srv_adm_st_chg_cause ca
!(s->flags & SRV_F_BACKUP) && s->next_eweight)
srv_shutdown_backup_streams(s->proxy, SF_ERR_UP);
- /* check if we can handle some connections queued at the proxy. We
- * will take as many as we can handle.
+ /* check if we can handle some connections queued.
+ * We will take as many as we can handle.
*/
- xferred = pendconn_grab_from_px(s);
+ process_srv_queue(s);
}
else if (s->next_admin & SRV_ADMF_MAINT) {
/* remaining in maintenance mode, let's inform precisely about the
--
1.7.10.4

View File

@ -0,0 +1,50 @@
From f0c756518e9bfabfb317d22aa3416bc84eb543ba Mon Sep 17 00:00:00 2001
From: Olivier Houchard <ohouchard@haproxy.com>
Date: Fri, 13 Dec 2024 17:11:05 +0000
Subject: [PATCH] BUG/MEDIUM: queues: Make sure we call process_srv_queue()
when leaving
In stream_free(), make sure we call process_srv_queue() each time we
call sess_change_server(), otherwise a server may end up not dequeuing
any stream when it could do so. In some extreme cases it could lead to
an infinite loop, as the server would appear to be available, as its
"served" parameter would be < maxconn, but would end up not being used,
as there are elements still in its queue.
This should be backported up to 2.6.
(cherry picked from commit dc9ce9c26469e00ab71fe6387dbd13010d4930f0)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 1385e4ca16b3797b0091a959b626935cd7f29b38)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 2de073ef00ee7d87aa82064dd2977645ec694730)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
Conflict:NA
Reference:https://git.haproxy.org/?p=haproxy-2.9.git;a=patch;h=f0c756518e9bfabfb317d22aa3416bc84eb543ba
---
src/stream.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/stream.c b/src/stream.c
index f4a3298..c42cf95 100644
--- a/src/stream.c
+++ b/src/stream.c
@@ -625,11 +625,14 @@ void stream_free(struct stream *s)
}
if (unlikely(s->srv_conn)) {
+ struct server *oldsrv = s->srv_conn;
/* the stream still has a reserved slot on a server, but
* it should normally be only the same as the one above,
* so this should not happen in fact.
*/
sess_change_server(s, NULL);
+ if (may_dequeue_tasks(oldsrv, s->be))
+ process_srv_queue(oldsrv);
}
/* We may still be present in the buffer wait queue */
--
1.7.10.4

View File

@ -0,0 +1,47 @@
From 56fb102c0c6094792fd38455b38b88a94454e996 Mon Sep 17 00:00:00 2001
From: Christopher Faulet <cfaulet@haproxy.com>
Date: Wed, 28 Aug 2024 15:42:22 +0200
Subject: [PATCH] BUG/MEDIUM: stream: Prevent mux upgrades if client connection
is no longer ready
If an early error occurred on the client connection, we must prevent any
multiplexer upgrades. Indeed, it is unexpected for a mux to be initialized
with no xprt. On a normal workflow it is impossible. So it is not an
issue. But if a mux upgrade is performed at the stream level, an early error
on the connection may have already been handled by the previous mux and the
connection may be already fully closed. If the mux upgrade is still
performed, a crash can be experienced.
It is possible to have a crash with an implicit TCP>HTTP upgrade if there is no
data in the input buffer. But it is also possible to get a crash with an
explicit "switch-mode http" rule.
It must be backported to all stable versions. In 2.2, the patch must be
applied directly in stream_set_backend() function.
(cherry picked from commit e4812404c541018ba521abf6573be92553ba7c53)
Signed-off-by: Willy Tarreau <w@1wt.eu>
(cherry picked from commit 13437097c312e524a346b9016d8ab273374d2053)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
Conflict: NA
Reference: https://github.com/haproxy/haproxy/commit/56fb102c0c6094792fd38455b38b88a94454e996
---
src/stream.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/stream.c b/src/stream.c
index e643a6db6a05..89b7c238fe48 100644
--- a/src/stream.c
+++ b/src/stream.c
@@ -1488,6 +1488,10 @@ int stream_set_http_mode(struct stream *s, const struct mux_proto_list *mux_prot
return 0;
conn = sc_conn(sc);
+
+ if (!sc_conn_ready(sc))
+ return 0;
+
if (conn) {
se_have_more_data(s->scf->sedesc);
/* Make sure we're unsubscribed, the the new

View File

@ -0,0 +1,56 @@
From 3e3b9eebf871510aee36c3a3336faac2f38c9559 Mon Sep 17 00:00:00 2001
From: Willy Tarreau <w@1wt.eu>
Date: Mon, 7 Apr 2025 15:30:43 +0200
Subject: [PATCH] BUG/MEDIUM: sample: fix risk of overflow when replacing
multiple regex back-refs
Aleandro Prudenzano of Doyensec and Edoardo Geraci of Codean Labs
reported a bug in sample_conv_regsub(), which can cause replacements
of multiple back-references to overflow the temporary trash buffer.
The problem happens when doing "regsub(match,replacement,g)": we're
replacing every occurrence of "match" with "replacement" in the input
sample, which requires a length check. For this, a max is applied, so
that a replacement may not use more than the remaining length in the
buffer. However, the length check is made on the replaced pattern and
not on the temporary buffer used to carry the new string. This results
in the remaining size to be usable for each input match, which can go
beyond the temporary buffer size if more than one occurrence has to be
replaced with something that's larger than the remaining room.
The fix proposed by Aleandro and Edoardo is the correct one (check on
"trash" not "output"), and is the one implemented in this patch.
While it is very unlikely that a config will replace multiple short
patterns each with a larger one in a request, this possibility cannot
be entirely ruled out (e.g. mask a known, short IP address using
"XXX.XXX.XXX.XXX"). However when this happens, the replacement pattern
will be static, and not be user-controlled, which is why this patch is
marked as medium.
The bug was introduced in 2.2 with commit 07e1e3c93e ("MINOR: sample:
regsub now supports backreferences"), so it must be backported to all
versions.
Special thanks go to Aleandro and Edoardo for reporting this bug with
a simple reproducer and a fix.
Conflict: NA
Reference: https://github.com/haproxy/haproxy/commit/3e3b9eebf871510aee36c3a3336faac2f38c9559
---
src/sample.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sample.c b/src/sample.c
index 1e2ff7d2ee8e8..980c27cb6a507 100644
--- a/src/sample.c
+++ b/src/sample.c
@@ -3168,7 +3168,7 @@ static int sample_conv_regsub(const struct arg *arg_p, struct sample *smp, void
output->data = exp_replace(output->area, output->size, start, arg_p[1].data.str.area, pmatch);
/* replace the matching part */
- max = output->size - output->data;
+ max = trash->size - trash->data;
if (max) {
if (max > output->data)
max = output->data;

View File

@ -5,7 +5,7 @@
Name: haproxy
Version: 2.9.5
Release: 4
Release: 9
Summary: The Reliable, High Performance TCP/HTTP Load Balancer
License: GPLv2+
@ -18,6 +18,14 @@ Source4: %{name}.sysconfig
Patch1: backport-BUG-MINOR-server-source-interface-ignored-from-defau.patch
Patch2: Backport-CVE-2024-45506-BUG-MAJOR-mux-h2-always.patch
Patch3: CVE-2024-49214.patch
Patch4: backport-BUG-MEDIUM-stream-Prevent-mux-upgrades-if-client-con.patch
Patch5: CVE-2024-53008-1.patch
Patch6: CVE-2024-53008-2.patch
Patch7: backport-BUG-MEDIUM-queues-Do-not-use-pendconn_grab_from_px.patch
Patch8: backport-BUG-MEDIUM-queues-Make-sure-we-call-process_srv_queu.patch
Patch9: backport-BUG-MEDIUM-queue-Make-process_srv_queue-return-the-n.patch
Patch10: backport-CVE-2025-32464.patch
BuildRequires: gcc lua-devel pcre2-devel openssl-devel systemd-devel systemd libatomic
Requires(pre): shadow-utils
@ -122,6 +130,32 @@ exit 0
%{_mandir}/man1/*
%changelog
* Tue Apr 29 2025 xinghe <xinghe2@h-partners.com> - 2.9.5-9
- Type:cves
- CVE:CVE-2025-32464
- SUG:NA
- DESC:fix CVE-2025-32464
* Mon Mar 17 2025 yanglu <yanglu72@h-partners.com> - 2.9.5-8
- Type:bugfix
- CVE:NA
- SUG:NA
- DESC:queues:Do not use pendconn_grab_from_px
queues:Make sure we call process_srv_queue when leaving
queue:Make process_srv_queue return the number of streams
* Tue Dec 10 2024 wangkai <13474090681@163.com> - 2.9.5-7
- Fix CVE-2024-53008
* Thu Nov 21 2024 xinghe <xinghe2@h-partners.com> - 2.9.5-6
- Type:bugfix
- CVE:NA
- SUG:NA
- DESC:stream: Prevent mux upgrades if client connection is no longer ready
* Mon Oct 14 2024 yaoxin <yao_xin001@hoperun.com> - 2.9.5-5
- Fix CVE-2024-49214
* Wed Sep 04 2024 yinyongkang <yinyongkang@kylinos.cn> - 2.9.5-4
- Type:CVE
- CVE:CVE-2024-45506