reject negative content-length headers

svn:r894
This commit is contained in:
Niels Provos 2008-07-02 04:22:48 +00:00
parent cb7c3bd671
commit 707f67849a
3 changed files with 78 additions and 7 deletions

View File

@ -113,6 +113,7 @@ Changes in current version:
o Support 64-bit integers in RPC structs o Support 64-bit integers in RPC structs
o Correct handling of trailing headers in chunked replies; from Scott Lamb. o Correct handling of trailing headers in chunked replies; from Scott Lamb.
o Support multi-line HTTP headers; based on a patch from Moshe Litvin o Support multi-line HTTP headers; based on a patch from Moshe Litvin
o Reject negative Content-Length headers; anonymous bug report
Changes in 1.4.0: Changes in 1.4.0:
o allow \r or \n individually to separate HTTP headers instead of the standard "\r\n"; from Charles Kerr. o allow \r or \n individually to separate HTTP headers instead of the standard "\r\n"; from Charles Kerr.

19
http.c
View File

@ -395,7 +395,7 @@ evhttp_make_header_request(struct evhttp_connection *evcon,
evhttp_find_header(req->output_headers, "Content-Length") == NULL){ evhttp_find_header(req->output_headers, "Content-Length") == NULL){
char size[12]; char size[12];
evutil_snprintf(size, sizeof(size), "%ld", evutil_snprintf(size, sizeof(size), "%ld",
(long)EVBUFFER_LENGTH(req->output_buffer)); (long)EVBUFFER_LENGTH(req->output_buffer));
evhttp_add_header(req->output_headers, "Content-Length", size); evhttp_add_header(req->output_headers, "Content-Length", size);
} }
} }
@ -726,6 +726,7 @@ evhttp_handle_chunked_read(struct evhttp_request *req, struct evbuffer *buf)
while ((len = EVBUFFER_LENGTH(buf)) > 0) { while ((len = EVBUFFER_LENGTH(buf)) > 0) {
if (req->ntoread < 0) { if (req->ntoread < 0) {
/* Read chunk size */ /* Read chunk size */
ev_int64_t ntoread;
char *p = evbuffer_readln(buf, NULL, EVBUFFER_EOL_CRLF); char *p = evbuffer_readln(buf, NULL, EVBUFFER_EOL_CRLF);
char *endp; char *endp;
int error; int error;
@ -736,13 +737,16 @@ evhttp_handle_chunked_read(struct evhttp_request *req, struct evbuffer *buf)
mm_free(p); mm_free(p);
continue; continue;
} }
req->ntoread = evutil_strtoll(p, &endp, 16); ntoread = evutil_strtoll(p, &endp, 16);
error = *p == '\0' || (*endp != '\0' && *endp != ' '); error = (*p == '\0' ||
(*endp != '\0' && *endp != ' ') ||
ntoread < 0);
mm_free(p); mm_free(p);
if (error) { if (error) {
/* could not get chunk size */ /* could not get chunk size */
return (DATA_CORRUPTED); return (DATA_CORRUPTED);
} }
req->ntoread = ntoread;
if (req->ntoread == 0) { if (req->ntoread == 0) {
/* Last chunk */ /* Last chunk */
return (ALL_DATA_READ); return (ALL_DATA_READ);
@ -1503,12 +1507,13 @@ evhttp_get_body_length(struct evhttp_request *req)
req->ntoread = -1; req->ntoread = -1;
} else { } else {
char *endp; char *endp;
req->ntoread = evutil_strtoll(content_length, &endp, 10); ev_int64_t ntoread = evutil_strtoll(content_length, &endp, 10);
if (*content_length == '\0' || *endp != '\0') { if (*content_length == '\0' || *endp != '\0' || ntoread < 0) {
event_warnx("%s: illegal content length: %s", event_debug(("%s: illegal content length: %s",
__func__, content_length); __func__, content_length));
return (-1); return (-1);
} }
req->ntoread = ntoread;
} }
event_debug(("%s: bytes to read: %d (in buffer %ld)\n", event_debug(("%s: bytes to read: %d (in buffer %ld)\n",

View File

@ -243,6 +243,12 @@ http_basic_cb(struct evhttp_request *req, void *arg)
test_ok++; test_ok++;
} }
} }
/* injecting a bad content-length */
if (evhttp_find_header(req->input_headers, "X-Negative"))
evhttp_add_header(req->output_headers,
"Content-Length", "-100");
/* allow sending of an empty reply */ /* allow sending of an empty reply */
evhttp_send_reply(req, HTTP_OK, "Everything is fine", evhttp_send_reply(req, HTTP_OK, "Everything is fine",
!empty ? evb : NULL); !empty ? evb : NULL);
@ -2183,6 +2189,64 @@ http_multi_line_header_test(void)
fprintf(stdout, "OK\n"); fprintf(stdout, "OK\n");
} }
static void
http_request_bad(struct evhttp_request *req, void *arg)
{
if (req != NULL) {
fprintf(stderr, "FAILED\n");
exit(1);
}
test_ok = 1;
event_loopexit(NULL);
}
static void
http_negative_content_length_test(void)
{
short port = -1;
struct evhttp_connection *evcon = NULL;
struct evhttp_request *req = NULL;
test_ok = 0;
fprintf(stdout, "Testing HTTP Negative Content Length: ");
http = http_setup(&port, NULL);
evcon = evhttp_connection_new("127.0.0.1", port);
if (evcon == NULL) {
fprintf(stdout, "FAILED\n");
exit(1);
}
/*
* At this point, we want to schedule a request to the HTTP
* server using our make request method.
*/
req = evhttp_request_new(http_request_bad, NULL);
/* Cause the response to have a negative content-length */
evhttp_add_header(req->output_headers, "X-Negative", "makeitso");
/* We give ownership of the request to the connection */
if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
fprintf(stdout, "FAILED\n");
exit(1);
}
event_dispatch();
evhttp_free(http);
if (test_ok != 1) {
fprintf(stdout, "FAILED\n");
exit(1);
}
fprintf(stdout, "OK\n");
}
void void
http_suite(void) http_suite(void)
{ {
@ -2204,6 +2268,7 @@ http_suite(void)
http_dispatcher_test(); http_dispatcher_test();
http_multi_line_header_test(); http_multi_line_header_test();
http_negative_content_length_test();
http_incomplete_test(0 /* use_timeout */); http_incomplete_test(0 /* use_timeout */);
http_incomplete_test(1 /* use_timeout */); http_incomplete_test(1 /* use_timeout */);