diff --git a/src/h2.c b/src/h2.c index f034e3d56..f35abc13a 100644 --- a/src/h2.c +++ b/src/h2.c @@ -45,6 +45,23 @@ struct h2_frame_definition h2_frame_definition[H2_FT_ENTRIES] = { [H2_FT_CONTINUATION ] = { .dir = 3, .min_id = 1, .max_id = H2_MAX_STREAM_ID, .min_len = 0, .max_len = H2_MAX_FRAME_LEN, }, }; +/* Looks into for forbidden characters for header values (0x00, 0x0A, + * 0x0D), starting at pointer which must be within . Returns + * non-zero if such a character is found, 0 otherwise. When run on unlikely + * header match, it's recommended to first check for the presence of control + * chars using ist_find_ctl(). + */ +static int has_forbidden_char(const struct ist ist, const char *start) +{ + do { + if ((uint8_t)*start <= 0x0d && + (1U << (uint8_t)*start) & ((1<<13) | (1<<10) | (1<<0))) + return 1; + start++; + } while (start < ist.ptr + ist.len); + return 0; +} + /* Parse the Content-Length header field of an HTTP/2 request. The function * checks all possible occurrences of a comma-delimited value, and verifies * if any of them doesn't match a previous value. It returns <0 if a value @@ -309,6 +326,7 @@ int h2_make_htx_request(struct http_hdr *list, struct htx *htx, unsigned int *ms uint32_t used = htx_used_space(htx); struct htx_sl *sl = NULL; unsigned int sl_flags = 0; + const char *ctl; lck = ck = -1; // no cookie for now fields = 0; @@ -327,6 +345,13 @@ int h2_make_htx_request(struct http_hdr *list, struct htx *htx, unsigned int *ms phdr = h2_str_to_phdr(list[idx].n); } + /* RFC7540#10.3: intermediaries forwarding to HTTP/1 must take care of + * rejecting NUL, CR and LF characters. + */ + ctl = ist_find_ctl(list[idx].v); + if (unlikely(ctl) && has_forbidden_char(list[idx].v, ctl)) + goto fail; + if (phdr > 0 && phdr < H2_PHDR_NUM_ENTRIES) { /* insert a pseudo header by its index (in phdr) and value (in value) */ if (fields & ((1 << phdr) | H2_PHDR_FND_NONE)) { @@ -553,6 +578,7 @@ int h2_make_htx_response(struct http_hdr *list, struct htx *htx, unsigned int *m uint32_t used = htx_used_space(htx); struct htx_sl *sl = NULL; unsigned int sl_flags = 0; + const char *ctl; fields = 0; for (idx = 0; list[idx].n.len != 0; idx++) { @@ -570,6 +596,13 @@ int h2_make_htx_response(struct http_hdr *list, struct htx *htx, unsigned int *m phdr = h2_str_to_phdr(list[idx].n); } + /* RFC7540#10.3: intermediaries forwarding to HTTP/1 must take care of + * rejecting NUL, CR and LF characters. + */ + ctl = ist_find_ctl(list[idx].v); + if (unlikely(ctl) && has_forbidden_char(list[idx].v, ctl)) + goto fail; + if (phdr > 0 && phdr < H2_PHDR_NUM_ENTRIES) { /* insert a pseudo header by its index (in phdr) and value (in value) */ if (fields & ((1 << phdr) | H2_PHDR_FND_NONE)) { @@ -675,6 +708,7 @@ int h2_make_htx_response(struct http_hdr *list, struct htx *htx, unsigned int *m */ int h2_make_htx_trailers(struct http_hdr *list, struct htx *htx) { + const char *ctl; uint32_t idx; int i; @@ -705,6 +739,13 @@ int h2_make_htx_trailers(struct http_hdr *list, struct htx *htx) isteq(list[idx].n, ist("transfer-encoding"))) goto fail; + /* RFC7540#10.3: intermediaries forwarding to HTTP/1 must take care of + * rejecting NUL, CR and LF characters. + */ + ctl = ist_find_ctl(list[idx].v); + if (unlikely(ctl) && has_forbidden_char(list[idx].v, ctl)) + goto fail; + if (!htx_add_trailer(htx, list[idx].n, list[idx].v)) goto fail; }