From 3add69df67863c806f074917a8efd8fbdfd31959 Mon Sep 17 00:00:00 2001 From: Niels Provos Date: Wed, 2 Jul 2008 04:28:12 +0000 Subject: [PATCH] from trunk: reject negative content-length headers svn:r895 --- ChangeLog | 1 + http.c | 19 ++++++++----- test/regress_http.c | 65 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index 06592af7..c7e7aceb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,7 @@ Changes in 1.4.6-stable: o switch all uses of [v]snprintf over to evutil 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 Reject negative Content-Length headers; anonymous bug report Changes in 1.4.5-stable: o Fix connection keep-alive behavior for HTTP/1.0 diff --git a/http.c b/http.c index 99122cb6..f4d4a4aa 100644 --- a/http.c +++ b/http.c @@ -391,7 +391,7 @@ evhttp_make_header_request(struct evhttp_connection *evcon, evhttp_find_header(req->output_headers, "Content-Length") == NULL){ char size[12]; 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); } } @@ -791,6 +791,7 @@ evhttp_handle_chunked_read(struct evhttp_request *req, struct evbuffer *buf) while ((len = EVBUFFER_LENGTH(buf)) > 0) { if (req->ntoread < 0) { /* Read chunk size */ + ev_int64_t ntoread; char *p = evbuffer_readline(buf); char *endp; int error; @@ -801,13 +802,16 @@ evhttp_handle_chunked_read(struct evhttp_request *req, struct evbuffer *buf) free(p); continue; } - req->ntoread = evutil_strtoll(p, &endp, 16); - error = *p == '\0' || (*endp != '\0' && *endp != ' '); + ntoread = evutil_strtoll(p, &endp, 16); + error = (*p == '\0' || + (*endp != '\0' && *endp != ' ') || + ntoread < 0); free(p); if (error) { /* could not get chunk size */ return (DATA_CORRUPTED); } + req->ntoread = ntoread; if (req->ntoread == 0) { /* Last chunk */ return (ALL_DATA_READ); @@ -1507,12 +1511,13 @@ evhttp_get_body_length(struct evhttp_request *req) req->ntoread = -1; } else { char *endp; - req->ntoread = evutil_strtoll(content_length, &endp, 10); - if (*content_length == '\0' || *endp != '\0') { - event_warnx("%s: illegal content length: %s", - __func__, content_length); + ev_int64_t ntoread = evutil_strtoll(content_length, &endp, 10); + if (*content_length == '\0' || *endp != '\0' || ntoread < 0) { + event_debug(("%s: illegal content length: %s", + __func__, content_length)); return (-1); } + req->ntoread = ntoread; } event_debug(("%s: bytes to read: %lld (in buffer %ld)\n", diff --git a/test/regress_http.c b/test/regress_http.c index c6a01234..282bd7fc 100644 --- a/test/regress_http.c +++ b/test/regress_http.c @@ -227,6 +227,12 @@ http_basic_cb(struct evhttp_request *req, void *arg) 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 */ evhttp_send_reply(req, HTTP_OK, "Everything is fine", !empty ? evb : NULL); @@ -1276,6 +1282,64 @@ http_multi_line_header_test(void) 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 http_suite(void) { @@ -1291,6 +1355,7 @@ http_suite(void) http_dispatcher_test(); http_multi_line_header_test(); + http_negative_content_length_test(); http_chunked_test(); }