diff --git a/doc/configuration.txt b/doc/configuration.txt index 780d6b505..67286440b 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -4108,7 +4108,7 @@ no option accept-invalid-http-request yes | yes | yes | no Arguments : none - By default, HAProxy complies with RFC2616 in terms of message parsing. This + By default, HAProxy complies with RFC7230 in terms of message parsing. This means that invalid characters in header names are not permitted and cause an error to be returned to the client. This is the desired behaviour as such forbidden characters are essentially used to build attacks exploiting server @@ -4121,7 +4121,9 @@ no option accept-invalid-http-request chars 0-31, 32 (space), 34 ('"'), 60 ('<'), 62 ('>'), 92 ('\'), 94 ('^'), 96 ('`'), 123 ('{'), 124 ('|'), 125 ('}'), 127 (delete) and anything above are not allowed at all. Haproxy always blocks a number of them (0..32, 127). The - remaining ones are blocked by default unless this option is enabled. + remaining ones are blocked by default unless this option is enabled. This + option also relaxes the test on the HTTP version format, it allows multiple + digits for both the major and the minor version. This option should never be enabled by default as it hides application bugs and open security breaches. It should only be deployed after a problem has @@ -4147,7 +4149,7 @@ no option accept-invalid-http-response yes | no | yes | yes Arguments : none - By default, HAProxy complies with RFC2616 in terms of message parsing. This + By default, HAProxy complies with RFC7230 in terms of message parsing. This means that invalid characters in header names are not permitted and cause an error to be returned to the client. This is the desired behaviour as such forbidden characters are essentially used to build attacks exploiting server @@ -4155,7 +4157,9 @@ no option accept-invalid-http-response server will emit invalid header names for whatever reason (configuration, implementation) and the issue will not be immediately fixed. In such a case, it is possible to relax HAProxy's header name parser to accept any character - even if that does not make sense, by specifying this option. + even if that does not make sense, by specifying this option. This option also + relaxes the test on the HTTP version format, it allows multiple digits for + both the major and the minor version. This option should never be enabled by default as it hides application bugs and open security breaches. It should only be deployed after a problem has diff --git a/src/proto_http.c b/src/proto_http.c index a34c11036..a0c9e1ce6 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -2943,6 +2943,25 @@ int http_wait_for_request(struct stream *s, struct channel *req, int an_bit) if (unlikely(msg->sl.rq.v_l == 0) && !http_upgrade_v09_to_v10(txn)) goto return_bad_req; + /* RFC7230#2.6 has enforced the format of the HTTP version string to be + * exactly one digit "." one digit. This check may be disabled using + * option accept-invalid-http-request. + */ + if (!(sess->fe->options2 & PR_O2_REQBUG_OK)) { + if (msg->sl.rq.v_l != 8) { + msg->err_pos = msg->sl.rq.v; + goto return_bad_req; + } + + if (req->buf->p[msg->sl.rq.v + 4] != '/' || + !isdigit((unsigned char)req->buf->p[msg->sl.rq.v + 5]) || + req->buf->p[msg->sl.rq.v + 6] != '.' || + !isdigit((unsigned char)req->buf->p[msg->sl.rq.v + 7])) { + msg->err_pos = msg->sl.rq.v + 4; + goto return_bad_req; + } + } + /* ... and check if the request is HTTP/1.1 or above */ if ((msg->sl.rq.v_l == 8) && ((req->buf->p[msg->sl.rq.v + 5] > '1') || @@ -6061,6 +6080,25 @@ int http_wait_for_response(struct stream *s, struct channel *rep, int an_bit) if (objt_server(s->target)) objt_server(s->target)->counters.p.http.rsp[n]++; + /* RFC7230#2.6 has enforced the format of the HTTP version string to be + * exactly one digit "." one digit. This check may be disabled using + * option accept-invalid-http-response. + */ + if (!(s->be->options2 & PR_O2_RSPBUG_OK)) { + if (msg->sl.st.v_l != 8) { + msg->err_pos = 0; + goto hdr_response_bad; + } + + if (rep->buf->p[4] != '/' || + !isdigit((unsigned char)rep->buf->p[5]) || + rep->buf->p[6] != '.' || + !isdigit((unsigned char)rep->buf->p[7])) { + msg->err_pos = 4; + goto hdr_response_bad; + } + } + /* check if the response is HTTP/1.1 or above */ if ((msg->sl.st.v_l == 8) && ((rep->buf->p[5] > '1') ||