webclient.c: Fix buffer overrun in HTTP header parsing
* Detect a long header line which doesn't fit the buffer. * If the header line in question doesn't seem important for us, just ignore it. * Otherwise, bail out with -E2BIG. The overrun was found by LLVM UBSan on macOS. The following is an example of a long header line, which doesn't fit the default CONFIG_WEBCLIENT_MAXHTTPLINE=200. ``` pacetanuki% curl -v https://www.midokura.com : : < HTTP/2 200 < server: nginx < date: Fri, 14 May 2021 02:16:24 GMT < content-type: text/html; charset=UTF-8 < content-length: 131313 < x-powered-by: PHP/7.4.18 < link: <https://www.midokura.com/wp-json/>; rel="https://api.w.org/", <https://www.midokura.com/wp-json/wp/v2/pages/7>; rel="alternate"; type="application/json", <https://www.midokura.com/>; rel=shortlink < x-powered-by: PleskLin < ```
This commit is contained in:
parent
eb1a99fdd6
commit
b53375074b
@ -152,6 +152,7 @@ struct wget_s
|
|||||||
|
|
||||||
char line[CONFIG_WEBCLIENT_MAXHTTPLINE];
|
char line[CONFIG_WEBCLIENT_MAXHTTPLINE];
|
||||||
int ndx;
|
int ndx;
|
||||||
|
bool skip_to_next_line;
|
||||||
|
|
||||||
#ifdef CONFIG_WEBCLIENT_GETMIMETYPE
|
#ifdef CONFIG_WEBCLIENT_GETMIMETYPE
|
||||||
char mimetype[CONFIG_WEBCLIENT_MAXMIMESIZE];
|
char mimetype[CONFIG_WEBCLIENT_MAXMIMESIZE];
|
||||||
@ -518,16 +519,35 @@ static inline int wget_parseheaders(struct wget_s *ws)
|
|||||||
|
|
||||||
while (offset < ws->datend)
|
while (offset < ws->datend)
|
||||||
{
|
{
|
||||||
|
bool got_nl;
|
||||||
|
|
||||||
ws->line[ndx] = ws->buffer[offset];
|
ws->line[ndx] = ws->buffer[offset];
|
||||||
if (ws->line[ndx] == ISO_NL)
|
got_nl = ws->line[ndx] == ISO_NL;
|
||||||
|
if (got_nl || ndx == CONFIG_WEBCLIENT_MAXHTTPLINE - 1)
|
||||||
{
|
{
|
||||||
/* We have an entire HTTP header line in s.line, so
|
bool found;
|
||||||
* we parse it.
|
|
||||||
|
if (ws->skip_to_next_line)
|
||||||
|
{
|
||||||
|
if (got_nl)
|
||||||
|
{
|
||||||
|
ws->skip_to_next_line = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ndx = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We have an entire HTTP header line in ws->line, or
|
||||||
|
* our buffer is already full, so we start parsing it.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
found = false;
|
||||||
if (ndx > 0) /* Should always be true */
|
if (ndx > 0) /* Should always be true */
|
||||||
{
|
{
|
||||||
ninfo("Got HTTP header line: %.*s\n", ndx - 1, &ws->line[0]);
|
ninfo("Got HTTP header line%s: %.*s\n",
|
||||||
|
got_nl ? "" : " (truncated)",
|
||||||
|
ndx - 1, &ws->line[0]);
|
||||||
if (ws->line[0] == ISO_CR)
|
if (ws->line[0] == ISO_CR)
|
||||||
{
|
{
|
||||||
/* This was the last header line (i.e., and empty "\r\n"),
|
/* This was the last header line (i.e., and empty "\r\n"),
|
||||||
@ -541,7 +561,30 @@ static inline int wget_parseheaders(struct wget_s *ws)
|
|||||||
|
|
||||||
/* Truncate the trailing \r\n */
|
/* Truncate the trailing \r\n */
|
||||||
|
|
||||||
ws->line[ndx - 1] = '\0';
|
if (got_nl)
|
||||||
|
{
|
||||||
|
ndx--;
|
||||||
|
if (ws->line[ndx] != ISO_CR)
|
||||||
|
{
|
||||||
|
nerr("ERROR: unexpected EOL from the server\n");
|
||||||
|
return -EPROTO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ws->line[ndx] = '\0';
|
||||||
|
|
||||||
|
if (!strchr(ws->line, ':'))
|
||||||
|
{
|
||||||
|
if (got_nl)
|
||||||
|
{
|
||||||
|
nerr("ERROR: invalid header possibly due to "
|
||||||
|
"small CONFIG_WEBCLIENT_MAXHTTPLINE\n");
|
||||||
|
return -E2BIG;
|
||||||
|
}
|
||||||
|
|
||||||
|
nerr("ERROR: invalid header\n");
|
||||||
|
return -EPROTO;
|
||||||
|
}
|
||||||
|
|
||||||
/* Check for specific HTTP header fields. */
|
/* Check for specific HTTP header fields. */
|
||||||
|
|
||||||
@ -559,6 +602,7 @@ static inline int wget_parseheaders(struct wget_s *ws)
|
|||||||
|
|
||||||
strncpy(ws->mimetype, ws->line + strlen(g_httpcontenttype),
|
strncpy(ws->mimetype, ws->line + strlen(g_httpcontenttype),
|
||||||
sizeof(ws->mimetype));
|
sizeof(ws->mimetype));
|
||||||
|
found = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
@ -573,14 +617,30 @@ static inline int wget_parseheaders(struct wget_s *ws)
|
|||||||
ret = parseurl(ws->line + strlen(g_httplocation), ws);
|
ret = parseurl(ws->line + strlen(g_httplocation), ws);
|
||||||
ninfo("New hostname='%s' filename='%s'\n",
|
ninfo("New hostname='%s' filename='%s'\n",
|
||||||
ws->hostname, ws->filename);
|
ws->hostname, ws->filename);
|
||||||
|
found = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We're done parsing this line, so we reset the index to the start
|
if (found && !got_nl)
|
||||||
* of the next line.
|
{
|
||||||
|
/* We found something we might care.
|
||||||
|
* but we couldn't process it correctly.
|
||||||
|
*/
|
||||||
|
|
||||||
|
nerr("ERROR: truncated a header due to "
|
||||||
|
"small CONFIG_WEBCLIENT_MAXHTTPLINE\n");
|
||||||
|
return -E2BIG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We're done parsing ws->line, so we reset the index.
|
||||||
|
*
|
||||||
|
* If we haven't seen the entire line yet (!got_nl),
|
||||||
|
* skip the rest of the line.
|
||||||
|
* Otherwise, start processing the next line.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ndx = 0;
|
ndx = 0;
|
||||||
|
ws->skip_to_next_line = !got_nl;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user