On Thu, Jun 18, 2020 at 6:03 PM Stefan Eissing
<[email protected]> wrote:
>
> ap_parse_request_line() for example, checks the initial HTTP/1.1 request line
> *and*
> the method names, uri, header_only and other request_rec fields.
>
> We can either copy the latter into mod_http2 and maintain it in two places or
> have a core function for it to be invoked by mod_http and mod_http2. That
> seems
> to be the design decision to make.
How about this attached patch?
ap_parse_request_line() will only parse the request line (extract the
fields, which mod_h2 doesn't need), and ap_check_request_line() will
do the validation.
Since ap_parse_request_line() becomes local to "protocol.c" only, it
can be unexported (not in this patch to avoid a MAJOR bump and thus
ease backport).
Thoughts?
Index: include/ap_mmn.h
===================================================================
--- include/ap_mmn.h (revision 1878986)
+++ include/ap_mmn.h (working copy)
@@ -632,6 +632,7 @@
* 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()
+ * 20200420.4 (2.5.1-dev) Add ap_check_request_line()
*/
#define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */
@@ -639,7 +640,7 @@
#ifndef MODULE_MAGIC_NUMBER_MAJOR
#define MODULE_MAGIC_NUMBER_MAJOR 20200420
#endif
-#define MODULE_MAGIC_NUMBER_MINOR 3 /* 0...n */
+#define MODULE_MAGIC_NUMBER_MINOR 4 /* 0...n */
/**
* Determine if the server's current MODULE_MAGIC_NUMBER is at least a
Index: include/http_protocol.h
===================================================================
--- include/http_protocol.h (revision 1878986)
+++ include/http_protocol.h (working copy)
@@ -68,13 +68,21 @@ AP_DECLARE(request_rec *) ap_create_request(conn_r
request_rec *ap_read_request(conn_rec *c);
/**
- * Parse and validate the request line.
+ * Parse the request line.
* @param r The current request
* @return 1 on success, 0 on failure
+ * TODO: unexport, used in trunk's protocol.c only
*/
AP_DECLARE(int) ap_parse_request_line(request_rec *r);
/**
+ * Validate the request line.
+ * @param r The current request
+ * @return 1 on success, 0 on failure
+ */
+AP_DECLARE(int) ap_check_request_line(request_rec *r);
+
+/**
* Validate the request header and select vhost.
* @param r The current request
* @return 1 on success, 0 on failure
Index: modules/http2/h2_request.c
===================================================================
--- modules/http2/h2_request.c (revision 1878986)
+++ modules/http2/h2_request.c (working copy)
@@ -278,8 +278,14 @@ request_rec *h2_request_create_rec(const h2_reques
/* Time to populate r with the data we have. */
r->request_time = req->request_time;
- r->the_request = apr_psprintf(r->pool, "%s %s HTTP/2.0",
- req->method, req->path ? req->path : "");
+ r->proto_num = HTTP_VERSION(2, 0);
+ r->protocol = apr_pstrdup(r->pool, "HTTP/2.0");
+ r->method = apr_pstrdup(r->pool, req->method ? req->method : "");
+ r->unparsed_uri = apr_pstrdup(r->pool, req->path ? req->path : "");
+ r->the_request = apr_pstrcat(r->pool,
+ r->method, " ",
+ r->unparsed_uri, " ",
+ r->protocol, NULL);
r->headers_in = apr_table_clone(r->pool, req->headers);
/* Start with r->hostname = NULL, ap_check_request_header() will get it
@@ -288,7 +294,7 @@ request_rec *h2_request_create_rec(const h2_reques
r->hostname = NULL;
/* Validate HTTP/1 request and select vhost. */
- if (!ap_parse_request_line(r) || !ap_check_request_header(r)) {
+ if (!ap_check_request_line(r) || !ap_check_request_header(r)) {
/* we may have switched to another server still */
r->per_dir_config = r->server->lookup_defaults;
access_status = r->status;
Index: server/protocol.c
===================================================================
--- server/protocol.c (revision 1878986)
+++ server/protocol.c (working copy)
@@ -610,7 +610,16 @@ AP_CORE_DECLARE(void) ap_parse_uri(request_rec *r,
{
int status = HTTP_OK;
- r->unparsed_uri = apr_pstrdup(r->pool, uri);
+ if (uri) {
+ r->unparsed_uri = apr_pstrdup(r->pool, uri);
+ }
+ else {
+ uri = r->unparsed_uri;
+ if (!uri) {
+ r->status = HTTP_INTERNAL_SERVER_ERROR;
+ return;
+ }
+ }
/* http://issues.apache.org/bugzilla/show_bug.cgi?id=31875
* http://issues.apache.org/bugzilla/show_bug.cgi?id=28450
@@ -751,7 +760,7 @@ AP_DECLARE(int) ap_parse_request_line(request_rec
rrl_badmethod09, rrl_reject09
} deferred_error = rrl_none;
apr_size_t len = 0;
- char *uri, *ll;
+ char *ll;
r->method = r->the_request;
@@ -782,7 +791,7 @@ AP_DECLARE(int) ap_parse_request_line(request_rec
if (!ll) {
if (deferred_error == rrl_none)
deferred_error = rrl_missinguri;
- r->protocol = uri = "";
+ r->protocol = r->unparsed_uri = "";
goto rrl_done;
}
else if (strict && ll[0] && apr_isspace(ll[1])
@@ -793,19 +802,20 @@ AP_DECLARE(int) ap_parse_request_line(request_rec
/* Advance uri pointer over leading whitespace, NUL terminate the method
* If non-SP whitespace is encountered, mark as specific error
*/
- for (uri = ll; apr_isspace(*uri); ++uri)
- if (*uri != ' ' && deferred_error == rrl_none)
+ r->unparsed_uri = ll;
+ for (; apr_isspace(*r->unparsed_uri); ++r->unparsed_uri)
+ if (*r->unparsed_uri != ' ' && deferred_error == rrl_none)
deferred_error = rrl_badwhitespace;
*ll = '\0';
-
- if (!*uri && deferred_error == rrl_none)
+
+ if (!*r->unparsed_uri && deferred_error == rrl_none)
deferred_error = rrl_missinguri;
/* Scan the URI up to the next whitespace, ensure it contains no raw
* control characters, otherwise mark in error
*/
- ll = (char*) ap_scan_vchar_obstext(uri);
- if (ll == uri || (*ll && !apr_isspace(*ll))) {
+ ll = (char*) ap_scan_vchar_obstext(r->unparsed_uri);
+ if (ll == r->unparsed_uri || (*ll && !apr_isspace(*ll))) {
deferred_error = rrl_baduri;
ll = strpbrk(ll, "\t\n\v\f\r ");
}
@@ -858,7 +868,8 @@ rrl_done:
/* For internal integrity and palloc efficiency, reconstruct the_request
* in one palloc, using only single SP characters, per spec.
*/
- r->the_request = apr_pstrcat(r->pool, r->method, *uri ? " " : NULL, uri,
+ r->the_request = apr_pstrcat(r->pool, r->method,
+ *r->unparsed_uri ? " " : NULL, r->unparsed_uri,
*r->protocol ? " " : NULL, r->protocol, NULL);
if (len == 8
@@ -897,15 +908,6 @@ rrl_done:
r->proto_num = HTTP_VERSION(0, 9);
}
- /* Determine the method_number and parse the uri prior to invoking error
- * handling, such that these fields are available for substitution
- */
- r->method_number = ap_method_number_of(r->method);
- if (r->method_number == M_GET && r->method[0] == 'H')
- r->header_only = 1;
-
- ap_parse_uri(r, uri);
-
/* With the request understood, we can consider HTTP/0.9 specific errors */
if (r->proto_num == HTTP_VERSION(0, 9) && deferred_error == rrl_none) {
if (conf->http09_enable == AP_HTTP09_DISABLE)
@@ -954,10 +956,41 @@ rrl_done:
"HTTP Request Line; Unrecognized protocol '%.*s' "
"(perhaps whitespace was injected?)",
field_name_len(r->protocol), r->protocol);
+
+ if (r->proto_num == HTTP_VERSION(0, 9)) {
+ /* Send all parsing and protocol error response with 1.x behavior,
+ * and reserve 505 errors for actual HTTP protocols presented.
+ * As called out in RFC7230 3.5, any errors parsing the protocol
+ * from the request line are nearly always misencoded HTTP/1.x
+ * requests. Only a valid 0.9 request with no parsing errors
+ * at all may be treated as a simple request, if allowed.
+ */
+ r->assbackwards = 0;
+ r->connection->keepalive = AP_CONN_CLOSE;
+ r->proto_num = HTTP_VERSION(1, 0);
+ r->protocol = "HTTP/1.0";
+ }
r->status = HTTP_BAD_REQUEST;
- goto rrl_failed;
+ return 0;
}
+ return 1;
+}
+
+AP_DECLARE(int) ap_check_request_line(request_rec *r)
+{
+ core_server_config *conf = ap_get_core_module_config(r->server->module_config);
+ int strict = (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE);
+
+ /* Determine the method_number and parse the uri prior to invoking error
+ * handling, such that these fields are available for substitution
+ */
+ r->method_number = ap_method_number_of(r->method);
+ if (r->method_number == M_GET && r->method[0] == 'H')
+ r->header_only = 1;
+
+ ap_parse_uri(r, NULL);
+
if (conf->http_methods == AP_HTTP_METHODS_REGISTERED
&& r->method_number == M_INVALID) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02423)
@@ -1453,7 +1486,9 @@ request_rec *ap_read_request(conn_rec *conn)
ap_run_pre_read_request(r, conn);
/* Get the request... */
- if (!read_request_line(r, tmp_bb) || !ap_parse_request_line(r)) {
+ if (!read_request_line(r, tmp_bb)
+ || !ap_parse_request_line(r)
+ || !ap_check_request_line(r)) {
apr_brigade_cleanup(tmp_bb);
switch (r->status) {
case HTTP_REQUEST_URI_TOO_LARGE: