BUG/MAJOR: h2: verify that :path starts with a '/' before concatenating it
authorWilly Tarreau <w@1wt.eu>
Tue, 10 Aug 2021 14:30:55 +0000 (16:30 +0200)
committerVincent Bernat <bernat@debian.org>
Sun, 5 Sep 2021 08:48:54 +0000 (09:48 +0100)
Tim Düsterhus found that while the H2 path is checked for non-emptiness,
invalid chars and '*', a test is missing to verify that except for '*',
it always starts with exactly one '/'. During the reconstruction of the
full URI when passing to HTX, this allows to affect the apparent authority
by appending a port number or a suffix name.

This only affects H2-to-H2 communications, as H2-to-H1 do not use the
authority. Like for previous fix, the following rule installed in the
frontend or backend is sufficient to renormalize the internal URI:

    http-request set-header host %[req.hdr(host)]

This needs to be backported to 2.2, since earlier versions do not rebuild
a full URI using the authority and will fail on the malformed path at the
HTTP layer.

(cherry picked from commit d3b22b75025246e81ff8d0c78837d4b89d7cf8f8)
Signed-off-by: Willy Tarreau <w@1wt.eu>
(cherry picked from commit 2360306269ff65420cba7c847687a774b1025ab5)
Signed-off-by: Willy Tarreau <w@1wt.eu>
(cherry picked from commit c99c5cd3588a28978cd065abc74508fe81a93a40)
Signed-off-by: Willy Tarreau <w@1wt.eu>
Gbp-Pq: Name 2.2-0003-BUG-MAJOR-h2-verify-that-path-starts-with-a-before-c.patch

src/h2.c

index bf44611a2c707d0d1039e9bc6eae79f32a02d210..2648488120769b0344249cac8f94ae0000322ac9 100644 (file)
--- a/src/h2.c
+++ b/src/h2.c
@@ -234,6 +234,22 @@ static struct htx_sl *h2_prepare_htx_reqline(uint32_t fields, struct ist *phdr,
                        htx->flags |= HTX_FL_PARSING_ERROR;
        }
 
+       if (fields & H2_PHDR_FND_PATH) {
+               /* 7540#8.1.2.3: :path must not be empty, and must be either
+                * '*' or an RFC3986 "path-absolute" starting with a "/" but
+                * not with "//".
+                */
+               if (unlikely(!phdr[H2_PHDR_IDX_PATH].len))
+                       goto fail;
+               else if (unlikely(phdr[H2_PHDR_IDX_PATH].ptr[0] != '/')) {
+                       if (!isteq(phdr[H2_PHDR_IDX_PATH], ist("*")))
+                               goto fail;
+               }
+               else if (phdr[H2_PHDR_IDX_PATH].len > 1 &&
+                        phdr[H2_PHDR_IDX_PATH].ptr[1] == '/')
+                       goto fail;
+       }
+
        if (!(flags & HTX_SL_F_HAS_SCHM)) {
                /* no scheme, use authority only (CONNECT) */
                uri = phdr[H2_PHDR_IDX_AUTH];
@@ -244,9 +260,6 @@ static struct htx_sl *h2_prepare_htx_reqline(uint32_t fields, struct ist *phdr,
                 * use the trash to concatenate them since all of them MUST fit
                 * in a bufsize since it's where they come from.
                 */
-               if (unlikely(!phdr[H2_PHDR_IDX_PATH].len))
-                       goto fail;   // 7540#8.1.2.3: :path must not be empty
-
                uri = ist2bin(trash.area, phdr[H2_PHDR_IDX_SCHM]);
                istcat(&uri, ist("://"), trash.size);
                istcat(&uri, phdr[H2_PHDR_IDX_AUTH], trash.size);