MINOR: mux-h1: Add global option accpet payload for any HTTP/1.0 requests
Since the 2.5, for security reason, HTTP/1.0 GET/HEAD/DELETE requests with a
payload are rejected (See e136bd12a "MEDIUM: mux-h1: Reject HTTP/1.0
GET/HEAD/DELETE requests with a payload" for details). However it may be an
issue for old clients.
To avoid any compatibility issue with such clients,
"h1-accept-payload-with-any-method" global option was added. It must only be
set if there is a good reason to do so because it may lead to a request
smuggling attack on some servers or intermediaries.
This patch should solve the issue #1691. it may be backported to 2.5.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index d528327..96210d8 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -1013,6 +1013,7 @@
- httpclient.resolvers.prefer
- httpclient.ssl.ca-file
- httpclient.ssl.verify
+ - h1-accept-payload-with-any-method
- h1-case-adjust
- h1-case-adjust-file
- insecure-fork-wanted
@@ -1450,6 +1451,20 @@
See also: grace
+h1-accept-payload-with-any-method
+ Does not reject HTTP/1.0 GET/HEAD/DELETE requests with a payload.
+
+ While It is explicitly allowed in HTTP/1.1, HTTP/1.0 is not clear on this
+ point and some old servers don't expect any payload and never look for body
+ length (via Content-Length or Transfer-Encoding headers). It means that some
+ intermediaries may properly handle the payload for HTTP/1.0 GET/HEAD/DELETE
+ requests, while some others may totally ignore it. That may lead to security
+ issues because a request smuggling attack is possible. Thus, by default,
+ HAProxy rejects HTTP/1.0 GET/HEAD/DELETE requests with a payload.
+
+ However, it may be an issue with some old clients. In this case, this global
+ option may be set.
+
h1-case-adjust <from> <to>
Defines the case adjustment to apply, when enabled, to the header name
<from>, to change it to <to> before sending it to HTTP/1 clients or
diff --git a/src/mux_h1.c b/src/mux_h1.c
index f9eba51..5085e11 100644
--- a/src/mux_h1.c
+++ b/src/mux_h1.c
@@ -149,7 +149,7 @@
/* Declare the headers map */
static struct h1_hdrs_map hdrs_map = { .name = NULL, .map = EB_ROOT };
-
+static int accept_payload_with_any_method = 0;
/* trace source and events */
static void h1_trace(enum trace_level level, uint64_t mask,
@@ -1602,12 +1602,14 @@
}
- /* Reject HTTP/1.0 GET/HEAD/DELETE requests with a payload. There is a
- * payload if the c-l is not null or the the payload is chunk-encoded.
- * A parsing error is reported but a A 413-Payload-Too-Large is returned
- * instead of a 400-Bad-Request.
+ /* Reject HTTP/1.0 GET/HEAD/DELETE requests with a payload except if
+ * accept_payload_with_any_method global option is set.
+ *There is a payload if the c-l is not null or the the payload is
+ * chunk-encoded. A parsing error is reported but a A
+ * 413-Payload-Too-Large is returned instead of a 400-Bad-Request.
*/
- if (!(h1m->flags & (H1_MF_RESP|H1_MF_VER_11)) &&
+ if (!accept_payload_with_any_method &&
+ !(h1m->flags & (H1_MF_RESP|H1_MF_VER_11)) &&
(((h1m->flags & H1_MF_CLEN) && h1m->body_len) || (h1m->flags & H1_MF_CHNK)) &&
(h1sl.rq.meth == HTTP_METH_GET || h1sl.rq.meth == HTTP_METH_HEAD || h1sl.rq.meth == HTTP_METH_DELETE)) {
h1s->flags |= H1S_F_PARSING_ERROR;
@@ -4137,6 +4139,17 @@
return err_code;
}
+/* config parser for global "h1-accept-payload_=-with-any-method" */
+static int cfg_parse_h1_accept_payload_with_any_method(char **args, int section_type, struct proxy *curpx,
+ const struct proxy *defpx, const char *file, int line,
+ char **err)
+{
+ if (too_many_args(0, args, err, NULL))
+ return -1;
+ accept_payload_with_any_method = 1;
+ return 0;
+}
+
/* config parser for global "h1-outgoing-header-case-adjust" */
static int cfg_parse_h1_header_case_adjust(char **args, int section_type, struct proxy *curpx,
@@ -4168,9 +4181,9 @@
return 0;
}
-
/* config keyword parsers */
static struct cfg_kw_list cfg_kws = {{ }, {
+ { CFG_GLOBAL, "h1-accept-payload-with-any-method", cfg_parse_h1_accept_payload_with_any_method },
{ CFG_GLOBAL, "h1-case-adjust", cfg_parse_h1_header_case_adjust },
{ CFG_GLOBAL, "h1-case-adjust-file", cfg_parse_h1_headers_case_adjust_file },
{ 0, NULL, NULL },