BUG/MINOR: h3: reject more chars from the :path pseudo header
authorWilly Tarreau <w@1wt.eu>
Tue, 8 Aug 2023 15:54:26 +0000 (17:54 +0200)
committerVincent Bernat <bernat@debian.org>
Fri, 3 Oct 2025 06:03:00 +0000 (08:03 +0200)
Origin: https://git.haproxy.org/?p=haproxy-2.6.git;a=commit;h=eacaa76e7b0e4182dfd17e1e7ca8c02c1cdab72c

This is the h3 version of this previous fix:

   BUG/MINOR: h2: reject more chars from the :path pseudo header

In addition to the current NUL/CR/LF, this will also reject all other
control chars, the space and '#' from the :path pseudo-header, to avoid
taking the '#' for a part of the path. It's still possible to fall back
to the previous behavior using "option accept-invalid-http-request".

Here the :path header value is scanned a second time to look for
forbidden chars because we don't know upfront if we're dealing with a
path header field or another one. This is no big deal anyway for now.

This should be progressively backported to 2.6, along with the
following commits it relies on (the same as for h2):

   REGTESTS: http-rules: add accept-invalid-http-request for normalize-uri tests
   REORG: http: move has_forbidden_char() from h2.c to http.h
   MINOR: ist: add new function ist_find_range() to find a character range
   MINOR: http: add new function http_path_has_forbidden_char()

(cherry picked from commit 2e97857a845540887a92029a566deb5b51f61d0b)
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
(cherry picked from commit 96dfea858edab8f1f63fa6e4df43f505b81fdad9)
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
(cherry picked from commit 97c15782afd9c70281ff0c72971485227494cc12)
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
Gbp-Pq: Name BUG-MINOR-h3-reject-more-chars-from-the-path-pseudo-.patch

src/h3.c

index d2ccee206c3c793ef48d3ad2f62d7f687eef0aa8..0e20f2c780abf293c8c0b85bb9dc636fee5af57e 100644 (file)
--- a/src/h3.c
+++ b/src/h3.c
@@ -402,6 +402,7 @@ static ssize_t h3_headers_to_htx(struct qcs *qcs, const struct buffer *buf,
        int hdr_idx, ret;
        int cookie = -1, last_cookie = -1, i;
        const char *ctl;
+       int relaxed = !!(h3c->qcc->proxy->options2 & PR_O2_REQBUG_OK);
 
        /* RFC 9114 4.1.2. Malformed Requests and Responses
         *
@@ -495,6 +496,19 @@ static ssize_t h3_headers_to_htx(struct qcs *qcs, const struct buffer *buf,
                                len = -1;
                                goto out;
                        }
+
+                       if (!relaxed) {
+                               /* we need to reject any control chars or '#' from the path,
+                                * unless option accept-invalid-http-request is set.
+                                */
+                               ctl = ist_find_range(list[hdr_idx].v, 0, '#');
+                               if (unlikely(ctl) && http_path_has_forbidden_char(list[hdr_idx].v, ctl)) {
+                                       TRACE_ERROR("forbidden character in ':path' pseudo-header", H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, qcs);
+                                       len = -1;
+                                       goto out;
+                               }
+                       }
+
                        path = list[hdr_idx].v;
                }
                else if (isteq(list[hdr_idx].n, ist(":scheme"))) {