summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYann Ylavic <ylavic@apache.org>2020-05-20 16:01:17 +0200
committerYann Ylavic <ylavic@apache.org>2020-05-20 16:01:17 +0200
commit11d03dc86a9642a4af44c40122299b7efad47775 (patch)
tree23576af687aa6d5ad87abb8307bb4e3006741f1e
parentlognos (diff)
downloadapache2-11d03dc86a9642a4af44c40122299b7efad47775.tar.xz
apache2-11d03dc86a9642a4af44c40122299b7efad47775.zip
core,modules: provide/use ap_parse_strict_length() helper.
It helps simplifying a lot of duplicated code based on apr_strtoff(), while also rejecting leading plus/minus signs which are dissalowed in Content-Length and (Content-)Range headers. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1877954 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--docs/log-message-tags/next-number2
-rw-r--r--include/ap_mmn.h3
-rw-r--r--include/httpd.h9
-rw-r--r--modules/apreq/filter.c8
-rw-r--r--modules/cache/mod_cache.c23
-rw-r--r--modules/cache/mod_cache_disk.c24
-rw-r--r--modules/cache/mod_cache_socache.c25
-rw-r--r--modules/dav/main/mod_dav.c23
-rw-r--r--modules/filters/mod_data.c4
-rw-r--r--modules/filters/mod_reflector.c11
-rw-r--r--modules/filters/mod_request.c12
-rw-r--r--modules/http/byterange_filter.c7
-rw-r--r--modules/http/http_filters.c18
-rw-r--r--modules/mappers/mod_negotiation.c8
-rw-r--r--modules/proxy/mod_proxy.c10
-rw-r--r--modules/proxy/mod_proxy_ajp.c19
-rw-r--r--modules/proxy/mod_proxy_http.c4
-rw-r--r--server/apreq_module_cgi.c9
-rw-r--r--server/util.c28
19 files changed, 118 insertions, 129 deletions
diff --git a/docs/log-message-tags/next-number b/docs/log-message-tags/next-number
index a45d5ebc99..70e6b624f9 100644
--- a/docs/log-message-tags/next-number
+++ b/docs/log-message-tags/next-number
@@ -1 +1 @@
-10242
+10244
diff --git a/include/ap_mmn.h b/include/ap_mmn.h
index 8ac21c15f4..2a8ce4c2ca 100644
--- a/include/ap_mmn.h
+++ b/include/ap_mmn.h
@@ -631,6 +631,7 @@
* 20200420.0 (2.5.1-dev) Add flags to listen_rec in place of use_specific_errors
* 20200420.1 (2.5.1-dev) Add ap_filter_adopt_brigade()
* 20200420.2 (2.5.1-dev) Add ap_proxy_worker_can_upgrade()
+ * 20200420.3 (2.5.1-dev) Add ap_parse_strict_length()
*/
#define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */
@@ -638,7 +639,7 @@
#ifndef MODULE_MAGIC_NUMBER_MAJOR
#define MODULE_MAGIC_NUMBER_MAJOR 20200420
#endif
-#define MODULE_MAGIC_NUMBER_MINOR 2 /* 0...n */
+#define MODULE_MAGIC_NUMBER_MINOR 3 /* 0...n */
/**
* Determine if the server's current MODULE_MAGIC_NUMBER is at least a
diff --git a/include/httpd.h b/include/httpd.h
index c3a81ba446..90293eb74c 100644
--- a/include/httpd.h
+++ b/include/httpd.h
@@ -2177,6 +2177,15 @@ AP_DECLARE(char *) ap_append_pid(apr_pool_t *p, const char *string,
const char *delim);
/**
+ * Parse a length string with decimal characters only, no leading sign nor
+ * trailing character, like Content-Length or (Content-)Range headers.
+ * @param len The parsed length (apr_off_t)
+ * @param str The string to parse
+ * @return 1 (success), 0 (failure)
+ */
+AP_DECLARE(int) ap_parse_strict_length(apr_off_t *len, const char *str);
+
+/**
* Parse a given timeout parameter string into an apr_interval_time_t value.
* The unit of the time interval is given as postfix string to the numeric
* string. Currently the following units are understood (case insensitive):
diff --git a/modules/apreq/filter.c b/modules/apreq/filter.c
index d2d8996eda..b4a1a4ebac 100644
--- a/modules/apreq/filter.c
+++ b/modules/apreq/filter.c
@@ -121,18 +121,16 @@ void apreq_filter_init_context(ap_filter_t *f)
}
cl_header = apr_table_get(r->headers_in, "Content-Length");
-
if (cl_header != NULL) {
- char *dummy;
- apr_uint64_t content_length = apr_strtoi64(cl_header, &dummy, 10);
+ apr_off_t cl;
- if (dummy == NULL || *dummy != 0) {
+ if (!ap_parse_strict_length(&cl, cl_header)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, r, APLOGNO(02045)
"Invalid Content-Length header (%s)", cl_header);
ctx->body_status = APREQ_ERROR_BADHEADER;
return;
}
- else if (content_length > ctx->read_limit) {
+ if ((apr_uint64_t)cl > ctx->read_limit) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, r, APLOGNO(02046)
"Content-Length header (%s) exceeds configured "
"max_body limit (%" APR_UINT64_T_FMT ")",
diff --git a/modules/cache/mod_cache.c b/modules/cache/mod_cache.c
index 46aec7e163..cb59aae992 100644
--- a/modules/cache/mod_cache.c
+++ b/modules/cache/mod_cache.c
@@ -1229,6 +1229,16 @@ static apr_status_t cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
return APR_SUCCESS;
}
+ /* Set the content length if known.
+ */
+ cl = apr_table_get(r->err_headers_out, "Content-Length");
+ if (cl == NULL) {
+ cl = apr_table_get(r->headers_out, "Content-Length");
+ }
+ if (cl && !ap_parse_strict_length(&size, cl)) {
+ reason = "invalid content length";
+ }
+
if (reason) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00768)
"cache: %s not cached for request %s. Reason: %s",
@@ -1251,19 +1261,6 @@ static apr_status_t cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
/* Make it so that we don't execute this path again. */
cache->in_checked = 1;
- /* Set the content length if known.
- */
- cl = apr_table_get(r->err_headers_out, "Content-Length");
- if (cl == NULL) {
- cl = apr_table_get(r->headers_out, "Content-Length");
- }
- if (cl) {
- char *errp;
- if (apr_strtoff(&size, cl, &errp, 10) || *errp || size < 0) {
- cl = NULL; /* parse error, see next 'if' block */
- }
- }
-
if (!cl) {
/* if we don't get the content-length, see if we have all the
* buckets and use their length to calculate the size
diff --git a/modules/cache/mod_cache_disk.c b/modules/cache/mod_cache_disk.c
index 7392b9981a..4b1e0227bf 100644
--- a/modules/cache/mod_cache_disk.c
+++ b/modules/cache/mod_cache_disk.c
@@ -1276,9 +1276,9 @@ static apr_status_t store_body(cache_handle_t *h, request_rec *r,
* sanity checks.
*/
if (seen_eos) {
- const char *cl_header = apr_table_get(r->headers_out, "Content-Length");
-
if (!dobj->disk_info.header_only) {
+ const char *cl_header;
+ apr_off_t cl;
if (dobj->data.tempfd) {
rv = apr_file_close(dobj->data.tempfd);
@@ -1297,6 +1297,7 @@ static apr_status_t store_body(cache_handle_t *h, request_rec *r,
apr_pool_destroy(dobj->data.pool);
return APR_EGENERAL;
}
+
if (dobj->file_size < dconf->minfs) {
ap_log_rerror(
APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00734) "URL %s failed the size check "
@@ -1305,17 +1306,16 @@ static apr_status_t store_body(cache_handle_t *h, request_rec *r,
apr_pool_destroy(dobj->data.pool);
return APR_EGENERAL;
}
- if (cl_header) {
- apr_int64_t cl = apr_atoi64(cl_header);
- if ((errno == 0) && (dobj->file_size != cl)) {
- ap_log_rerror(
- APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00735) "URL %s didn't receive complete response, not caching", h->cache_obj->key);
- /* Remove the intermediate cache file and return non-APR_SUCCESS */
- apr_pool_destroy(dobj->data.pool);
- return APR_EGENERAL;
- }
- }
+ cl_header = apr_table_get(r->headers_out, "Content-Length");
+ if (cl_header && (!ap_parse_strict_length(&cl, cl_header)
+ || cl != dobj->file_size)) {
+ ap_log_rerror(
+ APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00735) "URL %s didn't receive complete response, not caching", h->cache_obj->key);
+ /* Remove the intermediate cache file and return non-APR_SUCCESS */
+ apr_pool_destroy(dobj->data.pool);
+ return APR_EGENERAL;
+ }
}
/* All checks were fine, we're good to go when the commit comes */
diff --git a/modules/cache/mod_cache_socache.c b/modules/cache/mod_cache_socache.c
index cddda5dbbd..f369004a41 100644
--- a/modules/cache/mod_cache_socache.c
+++ b/modules/cache/mod_cache_socache.c
@@ -1046,7 +1046,8 @@ static apr_status_t store_body(cache_handle_t *h, request_rec *r,
/* Was this the final bucket? If yes, perform sanity checks.
*/
if (seen_eos) {
- const char *cl_header = apr_table_get(r->headers_out, "Content-Length");
+ const char *cl_header;
+ apr_off_t cl;
if (r->connection->aborted || r->no_cache) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02380)
@@ -1057,18 +1058,16 @@ static apr_status_t store_body(cache_handle_t *h, request_rec *r,
sobj->pool = NULL;
return APR_EGENERAL;
}
- if (cl_header) {
- apr_off_t cl;
- char *cl_endp;
- if (apr_strtoff(&cl, cl_header, &cl_endp, 10) != APR_SUCCESS
- || *cl_endp != '\0' || cl != sobj->body_length) {
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02381)
- "URL %s didn't receive complete response, not caching",
- h->cache_obj->key);
- apr_pool_destroy(sobj->pool);
- sobj->pool = NULL;
- return APR_EGENERAL;
- }
+
+ cl_header = apr_table_get(r->headers_out, "Content-Length");
+ if (cl_header && (!ap_parse_strict_length(&cl, cl_header)
+ || cl != sobj->body_length)) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02381)
+ "URL %s didn't receive complete response, not caching",
+ h->cache_obj->key);
+ apr_pool_destroy(sobj->pool);
+ sobj->pool = NULL;
+ return APR_EGENERAL;
}
/* All checks were fine, we're good to go when the commit comes */
diff --git a/modules/dav/main/mod_dav.c b/modules/dav/main/mod_dav.c
index d84db03e09..721c57a16b 100644
--- a/modules/dav/main/mod_dav.c
+++ b/modules/dav/main/mod_dav.c
@@ -814,7 +814,6 @@ static int dav_parse_range(request_rec *r,
char *range;
char *dash;
char *slash;
- char *errp;
range_c = apr_table_get(r->headers_in, "content-range");
if (range_c == NULL)
@@ -831,20 +830,19 @@ static int dav_parse_range(request_rec *r,
*dash++ = *slash++ = '\0';
/* detect invalid ranges */
- if (apr_strtoff(range_start, range + 6, &errp, 10)
- || *errp || *range_start < 0) {
+ if (!ap_parse_strict_length(range_start, range + 6)) {
return -1;
}
- if (apr_strtoff(range_end, dash, &errp, 10)
- || *errp || *range_end < 0 || *range_end < *range_start) {
+ if (!ap_parse_strict_length(range_end, dash)
+ || *range_end < *range_start) {
return -1;
}
if (*slash != '*') {
apr_off_t dummy;
- if (apr_strtoff(&dummy, slash, &errp, 10)
- || *errp || dummy <= *range_end) {
+ if (!ap_parse_strict_length(&dummy, slash)
+ || dummy <= *range_end) {
return -1;
}
}
@@ -2538,20 +2536,13 @@ static int process_mkcol_body(request_rec *r)
r->read_chunked = 1;
}
else if (lenp) {
- const char *pos = lenp;
-
- while (apr_isdigit(*pos) || apr_isspace(*pos)) {
- ++pos;
- }
-
- if (*pos != '\0') {
+ if (!ap_parse_strict_length(&r->remaining, lenp)) {
+ r->remaining = 0;
/* This supplies additional information for the default message. */
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00590)
"Invalid Content-Length %s", lenp);
return HTTP_BAD_REQUEST;
}
-
- r->remaining = apr_atoi64(lenp);
}
if (r->read_chunked || r->remaining > 0) {
diff --git a/modules/filters/mod_data.c b/modules/filters/mod_data.c
index d083d32204..ddadd1b360 100644
--- a/modules/filters/mod_data.c
+++ b/modules/filters/mod_data.c
@@ -107,8 +107,8 @@ static apr_status_t data_out_filter(ap_filter_t *f, apr_bucket_brigade *bb)
if (content_length) {
apr_off_t len, clen;
apr_brigade_length(ctx->bb, 1, &len);
- clen = apr_atoi64(content_length);
- if (clen >= 0 && clen < APR_INT32_MAX) {
+ if (ap_parse_strict_length(&clen, content_length)
+ && clen < APR_INT32_MAX) {
ap_set_content_length(r, len +
apr_base64_encode_len((int)clen) - 1);
}
diff --git a/modules/filters/mod_reflector.c b/modules/filters/mod_reflector.c
index 961092d0ea..5979cb8f72 100644
--- a/modules/filters/mod_reflector.c
+++ b/modules/filters/mod_reflector.c
@@ -91,11 +91,16 @@ static int reflector_handler(request_rec * r)
/* reflect the content length, if present */
if ((content_length = apr_table_get(r->headers_in, "Content-Length"))) {
- apr_off_t offset;
+ apr_off_t clen;
- apr_strtoff(&offset, content_length, NULL, 10);
- ap_set_content_length(r, offset);
+ if (!ap_parse_strict_length(&clen, content_length)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10243)
+ "reflector_handler: invalid content-length '%s'",
+ content_length);
+ return HTTP_BAD_REQUEST;
+ }
+ ap_set_content_length(r, clen);
}
/* reflect the content type, if present */
diff --git a/modules/filters/mod_request.c b/modules/filters/mod_request.c
index 21db7de36c..daa8e5977b 100644
--- a/modules/filters/mod_request.c
+++ b/modules/filters/mod_request.c
@@ -76,7 +76,6 @@ static apr_status_t keep_body_filter(ap_filter_t *f, apr_bucket_brigade *b,
if (!ctx) {
const char *lenp;
- char *endstr = NULL;
request_dir_conf *dconf = ap_get_module_config(f->r->per_dir_config,
&request_module);
@@ -93,13 +92,12 @@ static apr_status_t keep_body_filter(ap_filter_t *f, apr_bucket_brigade *b,
if (lenp) {
/* Protects against over/underflow, non-digit chars in the
- * string (excluding leading space) (the endstr checks)
- * and a negative number. */
- if (apr_strtoff(&ctx->remaining, lenp, &endstr, 10)
- || endstr == lenp || *endstr || ctx->remaining < 0) {
-
+ * string, leading plus/minus signs, trailing characters and
+ * a negative number.
+ */
+ if (!ap_parse_strict_length(&ctx->remaining, lenp)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, APLOGNO(01411)
- "Invalid Content-Length");
+ "Invalid Content-Length '%s'", lenp);
ap_remove_input_filter(f);
return bail_out_on_error(b, f, HTTP_REQUEST_ENTITY_TOO_LARGE);
diff --git a/modules/http/byterange_filter.c b/modules/http/byterange_filter.c
index 5ed90aaa91..8c5a670c6c 100644
--- a/modules/http/byterange_filter.c
+++ b/modules/http/byterange_filter.c
@@ -137,7 +137,6 @@ static int ap_set_byterange(request_rec *r, apr_off_t clength,
*indexes = apr_array_make(r->pool, ranges, sizeof(indexes_t));
while ((cur = ap_getword(r->pool, &range, ','))) {
char *dash;
- char *errp;
apr_off_t number, start, end;
if (!*cur)
@@ -154,7 +153,7 @@ static int ap_set_byterange(request_rec *r, apr_off_t clength,
if (dash == cur) {
/* In the form "-5" */
- if (apr_strtoff(&number, dash+1, &errp, 10) || *errp) {
+ if (!ap_parse_strict_length(&number, dash+1)) {
return 0;
}
if (number < 1) {
@@ -165,12 +164,12 @@ static int ap_set_byterange(request_rec *r, apr_off_t clength,
}
else {
*dash++ = '\0';
- if (apr_strtoff(&number, cur, &errp, 10) || *errp) {
+ if (!ap_parse_strict_length(&number, cur)) {
return 0;
}
start = number;
if (*dash) {
- if (apr_strtoff(&number, dash, &errp, 10) || *errp) {
+ if (!ap_parse_strict_length(&number, dash)) {
return 0;
}
end = number;
diff --git a/modules/http/http_filters.c b/modules/http/http_filters.c
index 5bebe2c500..016bcf5e97 100644
--- a/modules/http/http_filters.c
+++ b/modules/http/http_filters.c
@@ -364,16 +364,13 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
lenp = NULL;
}
if (lenp) {
- char *endstr;
-
ctx->state = BODY_LENGTH;
/* Protects against over/underflow, non-digit chars in the
- * string (excluding leading space) (the endstr checks)
- * and a negative number. */
- if (apr_strtoff(&ctx->remaining, lenp, &endstr, 10)
- || endstr == lenp || *endstr || ctx->remaining < 0) {
-
+ * string, leading plus/minus signs, trailing characters and
+ * a negative number.
+ */
+ if (!ap_parse_strict_length(&ctx->remaining, lenp)) {
ctx->remaining = 0;
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r, APLOGNO(01587)
"Invalid Content-Length");
@@ -1667,13 +1664,10 @@ AP_DECLARE(int) ap_setup_client_block(request_rec *r, int read_policy)
r->read_chunked = 1;
}
else if (lenp) {
- char *endstr;
-
- if (apr_strtoff(&r->remaining, lenp, &endstr, 10)
- || *endstr || r->remaining < 0) {
+ if (!ap_parse_strict_length(&r->remaining, lenp)) {
r->remaining = 0;
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(01594)
- "Invalid Content-Length");
+ "Invalid Content-Length '%s'", lenp);
return HTTP_BAD_REQUEST;
}
}
diff --git a/modules/mappers/mod_negotiation.c b/modules/mappers/mod_negotiation.c
index dc0211487c..c056b28455 100644
--- a/modules/mappers/mod_negotiation.c
+++ b/modules/mappers/mod_negotiation.c
@@ -988,19 +988,17 @@ static int read_type_map(apr_file_t **map, negotiation_state *neg,
has_content = 1;
}
else if (!strncmp(buffer, "content-length:", 15)) {
- char *errp;
- apr_off_t number;
+ apr_off_t clen;
body1 = ap_get_token(neg->pool, &body, 0);
- if (apr_strtoff(&number, body1, &errp, 10) != APR_SUCCESS
- || *errp || number < 0) {
+ if (!ap_parse_strict_length(&clen, body1)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00684)
"Parse error in type map, Content-Length: "
"'%s' in %s is invalid.",
body1, r->filename);
break;
}
- mime_info.bytes = number;
+ mime_info.bytes = clen;
has_content = 1;
}
else if (!strncmp(buffer, "content-language:", 17)) {
diff --git a/modules/proxy/mod_proxy.c b/modules/proxy/mod_proxy.c
index c0a44222c8..edbf1bcc94 100644
--- a/modules/proxy/mod_proxy.c
+++ b/modules/proxy/mod_proxy.c
@@ -1225,7 +1225,6 @@ static int proxy_handler(request_rec *r)
/* Did the scheme handler process the request? */
if (access_status != DECLINED) {
const char *cl_a;
- char *end;
apr_off_t cl;
/*
@@ -1235,18 +1234,17 @@ static int proxy_handler(request_rec *r)
if (access_status != HTTP_BAD_GATEWAY) {
goto cleanup;
}
+
cl_a = apr_table_get(r->headers_in, "Content-Length");
- if (cl_a) {
- apr_strtoff(&cl, cl_a, &end, 10);
+ if (cl_a && (!ap_parse_strict_length(&cl, cl_a)
+ || cl > 0)) {
/*
* The request body is of length > 0. We cannot
* retry with a direct connection since we already
* sent (parts of) the request body to the proxy
* and do not have any longer.
*/
- if (cl > 0) {
- goto cleanup;
- }
+ goto cleanup;
}
/*
* Transfer-Encoding was set as input header, so we had
diff --git a/modules/proxy/mod_proxy_ajp.c b/modules/proxy/mod_proxy_ajp.c
index aa8bc94d7e..9ae0f437da 100644
--- a/modules/proxy/mod_proxy_ajp.c
+++ b/modules/proxy/mod_proxy_ajp.c
@@ -126,11 +126,8 @@ static apr_off_t get_content_length(request_rec * r)
if (r->main == NULL) {
const char *clp = apr_table_get(r->headers_in, "Content-Length");
- if (clp) {
- char *errp;
- if (apr_strtoff(&len, clp, &errp, 10) || *errp || len < 0) {
- len = 0; /* parse error */
- }
+ if (clp && !ap_parse_strict_length(&len, clp)) {
+ len = -1; /* parse error */
}
}
@@ -253,10 +250,14 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r,
} else {
/* Get client provided Content-Length header */
content_length = get_content_length(r);
- status = ap_get_brigade(r->input_filters, input_brigade,
- AP_MODE_READBYTES, APR_BLOCK_READ,
- maxsize - AJP_HEADER_SZ);
-
+ if (content_length < 0) {
+ status = APR_EINVAL;
+ }
+ else {
+ status = ap_get_brigade(r->input_filters, input_brigade,
+ AP_MODE_READBYTES, APR_BLOCK_READ,
+ maxsize - AJP_HEADER_SZ);
+ }
if (status != APR_SUCCESS) {
/* We had a failure: Close connection to backend */
conn->close = 1;
diff --git a/modules/proxy/mod_proxy_http.c b/modules/proxy/mod_proxy_http.c
index 739050d2e3..f2857439d2 100644
--- a/modules/proxy/mod_proxy_http.c
+++ b/modules/proxy/mod_proxy_http.c
@@ -804,9 +804,7 @@ static int ap_proxy_http_prefetch(proxy_http_req_t *req,
}
else if (req->old_cl_val) {
if (r->input_filters == r->proto_input_filters) {
- char *endstr;
- status = apr_strtoff(&req->cl_val, req->old_cl_val, &endstr, 10);
- if (status != APR_SUCCESS || *endstr || req->cl_val < 0) {
+ if (!ap_parse_strict_length(&req->cl_val, req->old_cl_val)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01085)
"could not parse request Content-Length (%s)",
req->old_cl_val);
diff --git a/server/apreq_module_cgi.c b/server/apreq_module_cgi.c
index 3b03c378e0..779115dd6e 100644
--- a/server/apreq_module_cgi.c
+++ b/server/apreq_module_cgi.c
@@ -24,6 +24,8 @@
#include "apr_env.h"
#include "apreq_util.h"
+#include "httpd.h"
+
#define USER_DATA_KEY "apreq"
/* Parroting APLOG_* ... */
@@ -351,16 +353,15 @@ static void init_body(apreq_handle_t *handle)
apr_bucket *eos, *pipe;
if (cl_header != NULL) {
- char *dummy;
- apr_int64_t content_length = apr_strtoi64(cl_header, &dummy, 10);
+ apr_off_t content_length;
- if (dummy == NULL || *dummy != 0) {
+ if (!ap_parse_strict_length(&content_length, cl_header)) {
req->body_status = APREQ_ERROR_BADHEADER;
cgi_log_error(CGILOG_MARK, CGILOG_ERR, req->body_status, handle,
"Invalid Content-Length header (%s)", cl_header);
return;
}
- else if ((apr_uint64_t)content_length > req->read_limit) {
+ if ((apr_uint64_t)content_length > req->read_limit) {
req->body_status = APREQ_ERROR_OVERLIMIT;
cgi_log_error(CGILOG_MARK, CGILOG_ERR, req->body_status, handle,
"Content-Length header (%s) exceeds configured "
diff --git a/server/util.c b/server/util.c
index 7603895e02..59e273e911 100644
--- a/server/util.c
+++ b/server/util.c
@@ -2673,6 +2673,15 @@ AP_DECLARE(apr_status_t) ap_timeout_parameter_parse(
return APR_SUCCESS;
}
+AP_DECLARE(int) ap_parse_strict_length(apr_off_t *len, const char *str)
+{
+ char *end;
+
+ return (apr_isdigit(*str)
+ && apr_strtoff(len, str, &end, 10) == APR_SUCCESS
+ && *end == '\0');
+}
+
/**
* Determine if a request has a request body or not.
*
@@ -2682,20 +2691,13 @@ AP_DECLARE(apr_status_t) ap_timeout_parameter_parse(
AP_DECLARE(int) ap_request_has_body(request_rec *r)
{
apr_off_t cl;
- char *estr;
const char *cls;
- int has_body;
-
- has_body = (!r->header_only
- && (r->kept_body
- || apr_table_get(r->headers_in, "Transfer-Encoding")
- || ( (cls = apr_table_get(r->headers_in, "Content-Length"))
- && (apr_strtoff(&cl, cls, &estr, 10) == APR_SUCCESS)
- && (!*estr)
- && (cl > 0) )
- )
- );
- return has_body;
+
+ return (!r->header_only
+ && (r->kept_body
+ || apr_table_get(r->headers_in, "Transfer-Encoding")
+ || ((cls = apr_table_get(r->headers_in, "Content-Length"))
+ && ap_parse_strict_length(&cl, cls) && cl > 0)));
}
/**