MEDIUM: h1: Accept invalid T-E values with accept-invalid-http-response option

Since the 2.6, A parsing error is reported when the chunked encoding is
found twice. As stated in RFC9112, A sender must not apply the chunked
transfer coding more than once to a message body. It means only one chunked
coding must be found. In addition, empty values are also rejected becaues it
is forbidden by RFC9110.

However, in both cases, it may be useful to relax the rules for trusted
legacy servers when accept-invalid-http-response option is set. Especially
because it was accepted on 2.4 and older. In addition, T-E header is now
sanitized before sending it. It is not a problem Because it is a hop-by-hop
header

Note that it remains invalid on client side because there is no good reason
to relax the parsing on this side. We can argue a server is trusted so we
can decide to support some legacy behavior. It is not true on client side
and it is highly suspicious if a client is sending an invalid T-E header.

Note also we continue to reject unsupported T-E values (so all codings except
"chunked"). Because the "TE" header is sanitized and cannot contain other value
than "Trailers", there is absolutely no reason for a server to use something
else.

This patch should fix the issue #2677. It could probably be backported as
far as 2.6 if necessary.
This commit is contained in:
Christopher Faulet 2024-09-11 15:02:07 +02:00
parent 2b95c77c08
commit 1900ca475f
2 changed files with 27 additions and 17 deletions

View File

@ -128,17 +128,21 @@ int h1_parse_cont_len_header(struct h1m *h1m, struct ist *value)
* H1_MF_TE_OTHER flag is set if any other encoding is found. The H1_MF_XFER_ENC
* flag is always set. The H1_MF_CHNK is set when "chunked" encoding is the last
* one. Note that transfer codings are case-insensitive (cf RFC7230#4). This
* function returns <0 if a error is found, 0 if the whole header can be dropped
* (not used yet), or >0 if the value can be indexed.
* function returns -2 for a fatal error, -1 for an error that may be hiidden by
* config, 0 if the whole header can be dropped (not used yet), or >0 if the
* value can be indexed.
*/
int h1_parse_xfer_enc_header(struct h1m *h1m, struct ist value)
{
char *e, *n;
struct ist word;
int ret = 1;
/* Reject empty header */
if (istptr(value) == istend(value))
goto fail;
if (istptr(value) == istend(value)) {
ret = -1;
goto end;
}
h1m->flags |= H1_MF_XFER_ENC;
@ -154,7 +158,7 @@ int h1_parse_xfer_enc_header(struct h1m *h1m, struct ist value)
/* a comma at the end means the last value is empty */
if (n+1 == e)
goto fail;
ret = -1;
word.len = n - word.ptr;
/* trim trailing blanks */
@ -165,8 +169,7 @@ int h1_parse_xfer_enc_header(struct h1m *h1m, struct ist value)
/* empty values are forbidden */
if (!word.len)
goto fail;
ret = -1;
else if (isteqi(word, ist("chunked"))) {
if (h1m->flags & H1_MF_TE_CHUNKED) {
/* cf RFC7230#3.3.1 : A sender MUST NOT apply
@ -174,7 +177,7 @@ int h1_parse_xfer_enc_header(struct h1m *h1m, struct ist value)
* (i.e., chunking an already chunked message is
* not allowed)
*/
goto fail;
ret = -1;
}
h1m->flags |= (H1_MF_TE_CHUNKED|H1_MF_CHNK);
}
@ -186,7 +189,8 @@ int h1_parse_xfer_enc_header(struct h1m *h1m, struct ist value)
* as the final transfer coding to ensure that
* the message is properly framed.
*/
goto fail;
ret = -2;
goto end;
}
h1m->flags |= H1_MF_TE_OTHER;
}
@ -194,9 +198,8 @@ int h1_parse_xfer_enc_header(struct h1m *h1m, struct ist value)
word.ptr = n;
}
return 1;
fail:
return -1;
end:
return ret;
}
/* Validate the authority and the host header value for CONNECT method. If there
@ -1035,9 +1038,15 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
if (isteqi(n, ist("transfer-encoding"))) {
ret = h1_parse_xfer_enc_header(h1m, v);
if (ret < 0) {
state = H1_MSG_HDR_L2_LWS;
ptr = v.ptr; /* Set ptr on the error */
goto http_msg_invalid;
/* For the response only, don't report error if PR_O2_RSPBUG_OK is set
* and the error can be hidden */
if (ret == -2 || !(h1m->flags & H1_MF_RESP) || (h1m->err_pos < -1)) {
state = H1_MSG_HDR_L2_LWS;
ptr = v.ptr; /* Set ptr on the error */
goto http_msg_invalid;
}
if (h1m->err_pos == -1)
h1m->err_pos = ptr - start + skip;
}
else if (ret == 0) {
/* skip it */

View File

@ -2507,8 +2507,9 @@ static size_t h1_make_headers(struct h1s *h1s, struct h1m *h1m, struct htx *htx,
goto nextblk;
if (!(h1m->flags & H1_MF_CHNK))
goto nextblk;
if (h1_parse_xfer_enc_header(h1m, v) < 0)
goto error;
if (h1s->flags & H1S_F_HAVE_CHNK)
goto nextblk;
v = ist("chunked");
h1s->flags |= H1S_F_HAVE_CHNK;
}
else if (isteq(n, ist("content-length"))) {