From: Willy Tarreau Date: Fri, 30 Oct 2026 10:29:45 +0000 (+0100) Subject: MINOR: h3: pass the HTTP hardening checks to the h3s and h3c X-Git-Tag: v3.5-dev11~24 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=5e8bfc379a36364f578e5807efa6464899147c5b;p=haproxy.git MINOR: h3: pass the HTTP hardening checks to the h3s and h3c This adds an http_hdng_chk into h3s. In h3_attach() we preset it to the h3c's profile's mask, which is found during h3c initialization from the connection side's target profile (from server or bind_conf). If no hardening profile is found ("auto" or not set), then we check the source address of the connection. If it's a non-local IPv4/v6, then we use the untrusted profile, otherwise we fall back to the trusted one. Contrary to h2, h3s is allocated before parsing (like h1s), so we can place all pre-initialization in h3_attach() without having to preset any fields on h3s before parsing: in h3_rcv_buf(), we just have to preset and collect http_hdng_chk, like for h1 again. --- diff --git a/src/h3.c b/src/h3.c index aad29a72c..4f9921637 100644 --- a/src/h3.c +++ b/src/h3.c @@ -153,6 +153,7 @@ struct h3c { uint64_t id_shut_l; /* GOAWAY ID locally emitted */ uint64_t id_shut_r; /* GOAWAY ID emitted by the peer */ + const struct http_hdng_prf *hdng_prf; /* hardening profile for the connection, always set */ struct buffer_wait buf_wait; /* wait list for buffer allocations */ /* Stats counters */ struct h3_counters *prx_counters; @@ -177,6 +178,8 @@ struct h3s { unsigned long long body_len; /* known request body length from content-length header if present */ unsigned long long data_len; /* total length of all parsed DATA */ + struct http_hdng_chk hdng_chk; + int flags; int err; /* used for stream reset */ }; @@ -2131,11 +2134,22 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin) case H3_FT_HEADERS: if (h3s->st_req == H3S_ST_REQ_BEFORE) { if (!conn_is_back(qcs->qcc->conn)) { + /* set the global hardening context to our current one */ + http_hdng_chk = h3s->hdng_chk; ret = h3_req_headers_to_htx(qcs, b, flen, last_stream_frame); + /* and collect what was found */ + h3s->hdng_chk = http_hdng_chk; + http_reset_hardening_check(&http_hdng_chk); + h3s->st_req = H3S_ST_REQ_HEADERS; } else { + /* set the global hardening context to our current one */ + http_hdng_chk = h3s->hdng_chk; ret = h3_resp_headers_to_htx(qcs, b, flen, last_stream_frame); + /* and collect what was found */ + h3s->hdng_chk = http_hdng_chk; + http_reset_hardening_check(&http_hdng_chk); /* Check if an interim or final response was parsed. */ if (h3s->flags & H3_SF_RECV_INTERIM) { @@ -2157,7 +2171,12 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin) qcs->sd->kip = h3s->body_len; } else { + /* set the global hardening context to our current one */ + http_hdng_chk = h3s->hdng_chk; ret = h3_trailers_to_htx(qcs, b, flen, last_stream_frame); + /* and collect what was found */ + h3s->hdng_chk = http_hdng_chk; + http_reset_hardening_check(&http_hdng_chk); h3s->st_req = H3S_ST_REQ_TRAILERS; } @@ -3586,8 +3605,10 @@ static int h3_attach(struct qcs *qcs, void *conn_ctx) h3s->data_len = 0; h3s->flags = 0; h3s->err = 0; + http_reset_hardening_check(&h3s->hdng_chk); if (quic_stream_is_bidi(qcs->id)) { + h3s->hdng_chk.rules = h3c->hdng_prf->mask; h3s->type = H3S_T_REQ; h3s->st_req = H3S_ST_REQ_BEFORE; qcs_wait_http_req(qcs); @@ -3708,6 +3729,8 @@ static int h3_send_goaway(struct h3c *h3c) */ static int h3_init(struct qcc *qcc) { + const struct server *srv = objt_server(qcc->conn->target); + const struct listener *li = objt_listener(qcc->conn->target); struct h3c *h3c; TRACE_ENTER(H3_EV_H3C_NEW, qcc->conn); @@ -3727,6 +3750,20 @@ static int h3_init(struct qcc *qcc) qcc->ctx = h3c; h3c->prx_counters = qc_counters(qcc->conn->target, &h3_stats_module); + + if (srv) + h3c->hdng_prf = srv->hdng_prf; + else if (li) + h3c->hdng_prf = li->bind_conf->hdng_prf; + + /* "auto": untrusted for non-local otherwise trusted */ + if (!h3c->hdng_prf) { + if (conn_get_src(qcc->conn) && is_ip_untrusted(qcc->conn->src)) + h3c->hdng_prf = &http_hardening_untrusted; + else + h3c->hdng_prf = &http_hardening_trusted; + } + LIST_INIT(&h3c->buf_wait.list); TRACE_LEAVE(H3_EV_H3C_NEW, qcc->conn);