diff --git a/ChangeLog b/ChangeLog index b9a9e2cd..2e22a9ba 100644 --- a/ChangeLog +++ b/ChangeLog @@ -113,6 +113,7 @@ Changes in current version: o Support 64-bit integers in RPC structs 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.0: o allow \r or \n individually to separate HTTP headers instead of the standard "\r\n"; from Charles Kerr. diff --git a/http.c b/http.c index cb61608b..b5eac236 100644 --- a/http.c +++ b/http.c @@ -395,7 +395,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); } } @@ -726,6 +726,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_readln(buf, NULL, EVBUFFER_EOL_CRLF); char *endp; int error; @@ -736,13 +737,16 @@ evhttp_handle_chunked_read(struct evhttp_request *req, struct evbuffer *buf) mm_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); mm_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); @@ -1503,12 +1507,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: %d (in buffer %ld)\n", diff --git a/test/regress_http.c b/test/regress_http.c index 51c17ca8..bd4469bf 100644 --- a/test/regress_http.c +++ b/test/regress_http.c @@ -243,6 +243,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); @@ -2183,6 +2189,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) { @@ -2204,6 +2268,7 @@ http_suite(void) http_dispatcher_test(); http_multi_line_header_test(); + http_negative_content_length_test(); http_incomplete_test(0 /* use_timeout */); http_incomplete_test(1 /* use_timeout */);