Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 61 additions & 8 deletions src/reqs.c
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,14 @@ static int add_xtinyproxy_header (struct conn_s *connptr)
}
#endif /* XTINYPROXY */

static int
check_duplicate_header (pseudomap *hashofheaders, char *header, const char* kw)
{
return (!strcasecmp(header, kw) &&
pseudomap_find (hashofheaders, kw));
}


/*
* Take a complete header line and break it apart (into a key and the data.)
* Now insert this information into the hashmap for the connection so it
Expand All @@ -643,9 +651,9 @@ add_header_to_connection (pseudomap *hashofheaders, char *header, size_t len)
/* Calculate the new length of just the data */
len -= sep - header - 1;

/* prevent multiple content-length headers from being inserted */
if (!strcasecmp(header, "content-length") &&
pseudomap_find (hashofheaders, "content-length"))
/* prevent multiple CL/TE headers from being inserted */
if (check_duplicate_header(hashofheaders, header, "content-length") ||
check_duplicate_header(hashofheaders, header, "transfer-encoding"))
return 0;

return pseudomap_append (hashofheaders, header, sep);
Expand Down Expand Up @@ -826,11 +834,49 @@ static long get_content_length (pseudomap *hashofheaders)
return content_length;
}

static int is_chunked_transfer (pseudomap *hashofheaders)
{
/* In-place sanitize the Transfer-Encoding value by removing superfluous
whitespace.
Returns 1 if a valid trailing "chunked" transfer-coding is present.
Returns -1 if "chunked" is present but malformed / not final; otherwise 0. */
static int
check_chunked_and_sanitize_transfer_encoding(pseudomap *hashofheaders) {
char *data;
int was_comma = 0, ret = 0, c;
char *ins, *p, *chunked = 0;
data = pseudomap_find (hashofheaders, "transfer-encoding");
return data ? !strcasecmp (data, "chunked") : 0;
if (!data) return 0;
ins = p = data;
Comment thread
rofl0r marked this conversation as resolved.
while (*p) {
c = *(p++);
switch (c) {
case ',':
if (was_comma || chunked || ins == data) ret = -1;
if (!was_comma) *(ins++) = c;
was_comma = 1;
break;
case '\t': case ' ':
if (was_comma) {
if (was_comma == 1) *(ins++) = ' ';
++was_comma;
} else was_comma = 0;
break;
case 'C': case 'c':
if (!strncasecmp(p, "hunked", 6)) {
if (chunked || !(was_comma || ins == data))
ret = -1;
chunked = ins;
}
/* fall-through */
default:
was_comma = 0;
*(ins++) = c;
}
}
*ins = 0;
if (ret == -1) return ret;
/* after sanitization, if chunked was found, it needs to be the final coding */
if (chunked && (chunked[7] == 0 || chunked[7] == ';')) return 1;
return 0;
}

/*
Expand Down Expand Up @@ -921,8 +967,15 @@ process_client_headers (struct conn_s *connptr, pseudomap *hashofheaders)
*/
connptr->content_length.client = get_content_length (hashofheaders);

/* Check whether client sends chunked data. */
if (is_chunked_transfer (hashofheaders)) {
ret = check_chunked_and_sanitize_transfer_encoding(hashofheaders);
if (ret == -1) {
/* bad transfer-encoding: RFC 9112 6.1 */
indicate_http_error (connptr, 400,
"Bad Request",
NULL);
goto PULL_CLIENT_DATA;
} else if (ret == 1) {
/* well-formatted "chunked" transfer-encoding */
if (connptr->content_length.client != -1)
/* request smuggling, see GH issue #609 */
pseudomap_remove (hashofheaders, "content-length");
Expand Down
Loading