]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
Rename the previously undocumented HTTPProtocol directive
authorWilliam A. Rowe Jr <wrowe@apache.org>
Mon, 14 Nov 2016 17:01:20 +0000 (17:01 +0000)
committerWilliam A. Rowe Jr <wrowe@apache.org>
Mon, 14 Nov 2016 17:01:20 +0000 (17:01 +0000)
to EnforceHTTPProtocol, and invert the default behavior
to strictly observe RFC 7230 unless otherwise configured.
And Document This.

The relaxation option is renamed 'Unsafe'. 'Strict' is no
longer case sensitive. 'min=0.9|1.0' is now the verbose
'Allow0.9' or 'Require1.0' case-insenstive grammer. The
exclusivity tests have been modified to detect conflicts.

The 'strict,log' option failed to enforce strict conformance,
and has been removed. Unsafe, informational logging is possible
in any loadable module, after the request data is unsafely
accepted.

This triggers a group of failures in t/apache/headers.t as
expected since those patterns violated RFC 7230 section 3.2.4.
Submitted by: wrowe
Backport: r1756540

Correct AP_HTTP_CONFORMANCE_ flags
Submitted by: wrowe
Backport: r1756555

Renaming this directive to HttpProtocolOptions after discussion on dev@
Submitted by: wrowe
Backport: r1756649

Perform correct, strict parsing of the request line, handling the
http protocol tag, url and method appropriately, and attempting
to extract values even in the presence of unusual whitespace in
keeping with section 3.5, prior to responding with whatever
error reply is needed. Conforms to RFC7230 in all respects,
the section 3.5 optional behavior can be disabled by the user
with a new HttpProtocolOptions StrictWhitespace flag. In all
cases, the_request is regenerated from the parsed components
with exactly two space characters.

Shift sf's 'strict' method check from the Strict behavior because
it violates forward proxy logic, adding a new RegisteredMethods
flag, as it will certainly be useful to some.
Submitted by: wrowe
Backport: r1756729

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x-merge-http-strict@1769662 13f79535-47bb-0310-9956-ffa450edef68

docs/manual/mod/core.xml
include/ap_mmn.h
include/http_core.h
modules/http/http_filters.c
server/core.c
server/protocol.c
server/vhost.c

index 08c1b323b92bac0006a3841b826a78f00cf8553c..066bb845030932668bcef841104c8cac7ccc3f34 100644 (file)
@@ -1238,6 +1238,87 @@ EnableSendfile On
 </usage>
 </directivesynopsis>
 
+<directivesynopsis>
+<name>HTTPProtocolOptions</name>
+<description>Modify restrictions on HTTP Request Messages</description>
+<syntax>HTTPProtocolOptions [Strict|Unsafe] [Allow0.9|Require1.0] 
+[StrictWhitespace|LenientWhitespace] [RegisteredMethods|LenientMethods]</syntax>
+<default>HTTPProtocolOptions Strict Allow0.9 LenientWhitespace 
+LenientMethods</default>
+<contextlist><context>server config</context>
+<context>virtual host</context></contextlist>
+<compatibility>2.2.32 or 2.4.24 and later</compatibility>
+
+<usage>
+    <p>This directive changes the rules applied to the HTTP Request Line
+    (<a href="https://tools.ietf.org/html/rfc7230#section-3.1.1"
+      >RFC 7230 &sect;3.1.1</a>) and the HTTP Request Header Fields
+    (<a href="https://tools.ietf.org/html/rfc7230#section-3.2"
+      >RFC 7230 &sect;3.2</a>), which are now applied by default or using
+    the <code>Strict</code> option. Due to legacy modules, applications or
+    custom user-agents which must be deperecated, an <code>Unsafe</code>
+    option has been added to revert to the legacy behavior. These rules are
+    applied prior to request processing, so must be configured at the global
+    or default (first) matching virtual host section, by interface and not
+    by name, to be honored.</p>
+
+    <p>Prior to the introduction of this directive, the Apache HTTP Server
+    request message parsers were tolerant of a number of forms of input
+    which did not conform to the protocol.
+    <a href="https://tools.ietf.org/html/rfc7230#section-9.4"
+      >RFC 7230 &sect;9.4 Request Splitting</a> and
+    <a href="https://tools.ietf.org/html/rfc7230#section-9.5"
+      >&sect;9.5 Response Smuggling</a> call out only two of the potential
+    risks of accepting non-conformant request messages. As of the introduction
+    of this directive, all grammer rules of the specification are enforced in
+    the default <code>Strict</code> operating mode.</p>
+
+    <p>Users are strongly cautioned against toggling the <code>Unsafe</code>
+    mode of operation for these reasons, most especially on outward-facing,
+    publicly accessible server deployments. Reviewing the messages within the
+    <directive>ErrorLog</directive>, configured with
+    <directive>LogLevel</directive> <code>info</code> level or below,
+    can help identify such faulty requests along with their origin.
+    Users should pay particular attention to any 400 responses in the access
+    log for indiciations that valid requests are unexpectedly rejected.</p>
+
+    <p><a href="https://tools.ietf.org/html/rfc2616#section-19.6"
+         >RFC 2616 &sect;19.6</a> "Compatibility With Previous Versions" had
+    encouraged HTTP servers to support legacy HTTP/0.9 requests. RFC 7230
+    superceeds this with "The expectation to support HTTP/0.9 requests has
+    been removed" and offers additional comments in 
+    <a href="https://tools.ietf.org/html/rfc7230#appendix-A"
+      >RFC 2616 Appendix A</a>. The <code>Require1.0</code> option allows
+    the user to remove support of the default <code>Allow0.9</code> option's
+    behavior.</p>
+
+    <p><a href="https://tools.ietf.org/html/rfc7230#section-3.5"
+         >RFC 7230 &sect;3.5</a> "Message Parsing Robustness" permits, and
+    identifies potential risks of parsing messages containing non-space
+    character whitespace. While the spec defines that exactly one space
+    seperates the URI from the method, and the protocol from the URI, the
+    Apache HTTP Server has traditionally been lenient in accepting other
+    whitespace including one or more horizontal-tab or space characters.
+    The default <code>LenientWhitespace</code> continues to accept such
+    requests from non-conforming user-agents, but the administrator may toggle
+    the <code>StrictWhitespace</code> option to insist on precisely two spaces
+    in the request line. Other whitespace including vertical-tab, form-feed,
+    and carriage-return characters are rejected and cannot be supported.</p>
+
+    <p><a href="https://tools.ietf.org/html/rfc7231#section-4.1"
+         >RFC 7231 &sect;4.1</a> "Request Methods" "Overview" requires that
+    origin servers shall respond with an error when an unsupported method
+    is encountered in the request line. This already happens when the
+    <code>LenientMethods</code> option is used, but administrators may wish
+    to toggle the <code>RegisteredMethods</code> option and register all
+    permitted method tokens using the <directive>RegisterHttpMethod</directive>
+    directive, particularly if the <code>Unsafe</code> option has been toggled.
+    The <code>RegisteredMethods</code> option should <strong>not</strong>
+    be toggled for forward proxy hosts, as the methods supported by the
+    origin servers are unknown to the proxy server.</p>
+</usage>
+</directivesynopsis>
+
 <directivesynopsis>
 <name>Error</name>
 <description>Abort configuration parsing with a custom error message</description>
index 6fd27fe57a5dedaa767dbecc0c36682fe87859ec..7e1c19c3a74b427e8164e7956999449654239493 100644 (file)
  *                          Add http_conformance to core_server_config
  *                          Add ap_has_cntrl(), ap_get_http_token()
  *                          Add ap_scan_http_field_[content|token]()
+ *                          Added http_whitespace and http_methods to
+ *                          core_server_config
  */
 
 #define MODULE_MAGIC_COOKIE 0x41503234UL /* "AP24" */
index 89281cc6e65ecb0b11dd08234265e4beddd568f1..397f89137af4363c768b710fe4ca4f610b7096d2 100644 (file)
@@ -732,10 +732,20 @@ typedef struct {
     char http09_enable;
 
 #define AP_HTTP_CONFORMANCE_UNSET     0
-#define AP_HTTP_CONFORMANCE_LIBERAL   1
+#define AP_HTTP_CONFORMANCE_UNSAFE    1
 #define AP_HTTP_CONFORMANCE_STRICT    2
-#define AP_HTTP_CONFORMANCE_LOGONLY   4
     char http_conformance;
+
+#define AP_HTTP_WHITESPACE_UNSET      0
+#define AP_HTTP_WHITESPACE_LENIENT    1
+#define AP_HTTP_WHITESPACE_STRICT     2
+    char http_whitespace;
+
+#define AP_HTTP_METHODS_UNSET         0
+#define AP_HTTP_METHODS_LENIENT       1
+#define AP_HTTP_METHODS_REGISTERED    2
+    char http_methods;
+
 } core_server_config;
 
 /* for AddOutputFiltersByType in core.c */
index 8f35b5751c93133c3de2e740354237330166c2c4..cf26bee1e65e44a42dbec67fdd170ad30fe2e17b 100644 (file)
@@ -1302,9 +1302,9 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f,
     }
 
     conf = ap_get_core_module_config(r->server->module_config);
-    if (conf->http_conformance & AP_HTTP_CONFORMANCE_STRICT) {
+    if (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE) {
         int ok = check_headers(r);
-        if (!ok && !(conf->http_conformance & AP_HTTP_CONFORMANCE_LOGONLY)) {
+        if (!ok) {
             ap_die(HTTP_INTERNAL_SERVER_ERROR, r);
             return AP_FILTER_ERROR;
         }
index c277af3bb71cbed6b79b5ef15915ce3e0a45b98b..d988ad574292994f4bf3141c83b67333a3668859 100644 (file)
@@ -525,6 +525,12 @@ static void *merge_core_server_configs(apr_pool_t *p, void *basev, void *virtv)
     if (virt->http_conformance != AP_HTTP_CONFORMANCE_UNSET)
         conf->http_conformance = virt->http_conformance;
 
+    if (virt->http_whitespace != AP_HTTP_WHITESPACE_UNSET)
+        conf->http_whitespace = virt->http_whitespace;
+
+    if (virt->http_methods != AP_HTTP_METHODS_UNSET)
+        conf->http_methods = virt->http_methods;
+
     /* no action for virt->accf_map, not allowed per-vhost */
 
     if (virt->protocol)
@@ -3901,38 +3907,53 @@ static const char *set_protocols_honor_order(cmd_parms *cmd, void *dummy,
     return NULL;
 }
 
-static const char *set_http_protocol(cmd_parms *cmd, void *dummy,
-                                     const char *arg)
+static const char *set_http_protocol_options(cmd_parms *cmd, void *dummy,
+                                             const char *arg)
 {
     core_server_config *conf =
         ap_get_core_module_config(cmd->server->module_config);
 
-    if (strncmp(arg, "min=", 4) == 0) {
-        arg += 4;
-        if (strcmp(arg, "0.9") == 0)
-            conf->http09_enable = AP_HTTP09_ENABLE;
-        else if (strcmp(arg, "1.0") == 0)
-            conf->http09_enable = AP_HTTP09_DISABLE;
-        else
-            return "HttpProtocol 'min' must be one of '0.9' and '1.0'";
-        return NULL;
-    }
-
-    if (strcmp(arg, "strict") == 0)
-        conf->http_conformance = AP_HTTP_CONFORMANCE_STRICT;
-    else if (strcmp(arg, "strict,log-only") == 0)
-        conf->http_conformance = AP_HTTP_CONFORMANCE_STRICT|
-                                 AP_HTTP_CONFORMANCE_LOGONLY;
-    else if (strcmp(arg, "liberal") == 0)
-        conf->http_conformance = AP_HTTP_CONFORMANCE_LIBERAL;
+    if (strcasecmp(arg, "allow0.9") == 0)
+        conf->http09_enable |= AP_HTTP09_ENABLE;
+    else if (strcasecmp(arg, "require1.0") == 0)
+        conf->http09_enable |= AP_HTTP09_DISABLE;
+    else if (strcasecmp(arg, "strict") == 0)
+        conf->http_conformance |= AP_HTTP_CONFORMANCE_STRICT;
+    else if (strcasecmp(arg, "unsafe") == 0)
+        conf->http_conformance |= AP_HTTP_CONFORMANCE_UNSAFE;
+    else if (strcasecmp(arg, "strictwhitespace") == 0)
+        conf->http_whitespace |= AP_HTTP_WHITESPACE_STRICT;
+    else if (strcasecmp(arg, "lenientwhitespace") == 0)
+        conf->http_whitespace |= AP_HTTP_WHITESPACE_LENIENT;
+    else if (strcasecmp(arg, "registeredmethods") == 0)
+        conf->http_methods |= AP_HTTP_METHODS_REGISTERED;
+    else if (strcasecmp(arg, "lenientmethods") == 0)
+        conf->http_methods |= AP_HTTP_METHODS_LENIENT;
     else
-        return "HttpProtocol accepts 'min=0.9', 'min=1.0', 'liberal', "
-               "'strict', 'strict,log-only'";
-
-    if ((conf->http_conformance & AP_HTTP_CONFORMANCE_STRICT) &&
-        (conf->http_conformance & AP_HTTP_CONFORMANCE_LIBERAL)) {
-        return "HttpProtocol 'strict' and 'liberal' are mutually exclusive";
-    }
+        return "HttpProtocolOptions accepts 'Allow0.9' (default) or "
+               "'Require1.0', 'Unsafe' or 'Strict' (default), "
+               "'StrictWhitespace' or 'LenientWhitespace (default), and "
+               "'RegisteredMethods' or 'LenientMethods (default)";
+
+    if ((conf->http09_enable & AP_HTTP09_ENABLE)
+            && (conf->http09_enable & AP_HTTP09_DISABLE))
+        return "HttpProtocolOptions 'Allow0.9' and 'Require1.0'"
+               " are mutually exclusive";
+
+    if ((conf->http_conformance & AP_HTTP_CONFORMANCE_STRICT)
+            && (conf->http_conformance & AP_HTTP_CONFORMANCE_UNSAFE))
+        return "HttpProtocolOptions 'Strict' and 'Unsafe'"
+               " are mutually exclusive";
+
+    if ((conf->http_whitespace & AP_HTTP_WHITESPACE_STRICT)
+            && (conf->http_whitespace & AP_HTTP_WHITESPACE_LENIENT))
+        return "HttpProtocolOptions 'StrictWhitespace' and 'LenientWhitespace'"
+               " are mutually exclusive";
+
+    if ((conf->http_methods & AP_HTTP_METHODS_REGISTERED)
+            && (conf->http_methods & AP_HTTP_METHODS_LENIENT))
+        return "HttpProtocolOptions 'RegisteredMethods' and 'LenientMethods'"
+               " are mutually exclusive";
 
     return NULL;
 }
@@ -4470,9 +4491,9 @@ AP_INIT_ITERATE("Protocols", set_protocols, NULL, RSRC_CONF,
 AP_INIT_TAKE1("ProtocolsHonorOrder", set_protocols_honor_order, NULL, RSRC_CONF,
               "'off' (default) or 'on' to respect given order of protocols, "
               "by default the client specified order determines selection"),
-AP_INIT_ITERATE("HttpProtocol", set_http_protocol, NULL, RSRC_CONF,
-              "'min=0.9' (default) or 'min=1.0' to allow/deny HTTP/0.9; "
-              "'liberal', 'strict', 'strict,log-only'"),
+AP_INIT_ITERATE("HttpProtocolOptions", set_http_protocol_options, NULL, RSRC_CONF,
+              "'Allow0.9' or 'Require1.0' (default) to allow or deny HTTP/0.9; "
+              "'Unsafe' or 'Strict' (default) to process incorrect requests"),
 AP_INIT_ITERATE("RegisterHttpMethod", set_http_method, NULL, RSRC_CONF,
                 "Registers non-standard HTTP methods"),
 { NULL }
index d8a2ae1cb64490d646a3444d6373f0dd90e352dc..e861b418d4a694dd81142c2a55ac71731a3b82b6 100644 (file)
@@ -559,19 +559,32 @@ AP_CORE_DECLARE(void) ap_parse_uri(request_rec *r, const char *uri)
     }
 }
 
-static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
+/* get the length of the field name for logging, but no more than 80 bytes */
+#define LOG_NAME_MAX_LEN 80
+static int field_name_len(const char *field)
 {
-    const char *ll;
-    const char *uri;
-    const char *pro;
+    const char *end = ap_strchr_c(field, ':');
+    if (end == NULL || end - field > LOG_NAME_MAX_LEN)
+        return LOG_NAME_MAX_LEN;
+    return end - field;
+}
 
+static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
+{
+    enum {
+        rrl_none, rrl_badmethod, rrl_badwhitespace, rrl_excesswhitespace,
+        rrl_missinguri, rrl_badprotocol, rrl_trailingtext
+    } deferred_error = rrl_none;
+    char *ll;
+    char *uri;
     unsigned int major = 1, minor = 0;   /* Assume HTTP/1.0 if non-"HTTP" protocol */
     char http[5];
     apr_size_t len;
     int num_blank_lines = DEFAULT_LIMIT_BLANK_LINES;
     core_server_config *conf = ap_get_core_module_config(r->server->module_config);
-    int strict = conf->http_conformance & AP_HTTP_CONFORMANCE_STRICT;
-    int enforce_strict = !(conf->http_conformance & AP_HTTP_CONFORMANCE_LOGONLY);
+    int strict = (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE);
+    const char *badwhitespace = strict ? "\t\n\v\f\r" : "\n\v\f\r";
+    int strictspaces = (conf->http_whitespace == AP_HTTP_WHITESPACE_STRICT);
 
     /* Read past empty lines until we get a real request line,
      * a read error, the connection closes (EOF), or we timeout.
@@ -627,115 +640,212 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
     }
 
     r->request_time = apr_time_now();
-    ll = r->the_request;
-    r->method = ap_getword_white(r->pool, &ll);
 
-    uri = ap_getword_white(r->pool, &ll);
+    r->method = r->the_request;
+    if (apr_isspace(*r->method))
+        deferred_error = rrl_excesswhitespace; 
+    for ( ; apr_isspace(*r->method); ++r->method)
+        ; 
 
-    if (!*r->method || !*uri) {
-        r->status    = HTTP_BAD_REQUEST;
-        r->proto_num = HTTP_VERSION(1,0);
-        r->protocol  = apr_pstrdup(r->pool, "HTTP/1.0");
-        return 0;
+    if (strict) {
+        ll = (char*) ap_scan_http_token(r->method);
+        if ((ll == r->method) || (*ll && !apr_isspace(*ll))) {
+            deferred_error = rrl_badmethod;
+            ll = strpbrk(ll, "\t\n\v\f\r ");
+        }
     }
+    else {
+        ll = strpbrk(r->method, "\t\n\v\f\r ");
+    }
+    if (!ll) {
+        if (deferred_error == rrl_none)
+            deferred_error = rrl_missinguri;
+        r->protocol = uri = "";
+        len = 0;
+        goto rrl_done;
+    }
+
+    if (strictspaces && ll[0] && (ll[0] != ' ' || apr_isspace(ll[1]))
+            && deferred_error == rrl_none) {
+        deferred_error = rrl_excesswhitespace; 
+    }
+    for (uri = ll; apr_isspace(*uri); ++uri) 
+        if (strchr(badwhitespace, *uri) && deferred_error == rrl_none)
+            deferred_error = rrl_badwhitespace; 
+    *ll = '\0';
+    if (!*uri && deferred_error == rrl_none)
+        deferred_error = rrl_missinguri;
+
+    if (!(ll = strpbrk(uri, " \t\n\v\f\r"))) {
+        r->protocol = "";
+        len = 0;
+        goto rrl_done;
+    }
+    for (r->protocol = ll; apr_isspace(*r->protocol); ++r->protocol) 
+        if (strchr(badwhitespace, *r->protocol) && deferred_error == rrl_none
+                && deferred_error == rrl_none)
+            deferred_error = rrl_badwhitespace; 
+    *ll = '\0';
+    if (!(ll = strpbrk(r->protocol, " \t\n\v\f\r"))) {
+        len = strlen(r->protocol);
+        goto rrl_done;
+    }
+    len = ll - r->protocol;
+    if (strictspaces && *ll)
+        deferred_error = rrl_excesswhitespace; 
+    for ( ; apr_isspace(*ll); ++ll)
+        if (strchr(badwhitespace, *ll) && deferred_error == rrl_none)
+            deferred_error = rrl_badwhitespace; 
+    if (*ll && deferred_error == rrl_none)
+        deferred_error = rrl_trailingtext;
+    ll = (char *)r->protocol + len;
+    *ll = '\0';
+
+rrl_done:
+    /* For internal integrety, reconstruct the_request
+     * using only single SP characters, per spec.
+     * Once the method, uri and protocol are processed,
+     * we can then resume deferred error reporting
+     */
+    r->the_request = apr_pstrcat(r->pool, r->method, *uri ? " " : NULL, uri,
+                                 *r->protocol ? " " : NULL, r->protocol, NULL);
 
-    /* Provide quick information about the request method as soon as known */
+    if (len == 8
+            && r->protocol[0] == 'H' && r->protocol[1] == 'T'
+            && r->protocol[2] == 'T' && r->protocol[3] == 'P'
+            && r->protocol[4] == '/' && apr_isdigit(r->protocol[5])
+            && r->protocol[6] == '.' && apr_isdigit(r->protocol[7])
+            && r->protocol[5] != '0') {
+        r->assbackwards = 0;
+        r->proto_num = HTTP_VERSION(r->protocol[5] - '0', r->protocol[7] - '0');
+    }
+    else if (len == 8
+                 && (r->protocol[0] == 'H' || r->protocol[0] == 'h')
+                 && (r->protocol[1] == 'T' || r->protocol[1] == 't')
+                 && (r->protocol[2] == 'T' || r->protocol[2] == 't')
+                 && (r->protocol[3] == 'P' || r->protocol[3] == 'p')
+                 && r->protocol[4] == '/' && apr_isdigit(r->protocol[5])
+                 && r->protocol[6] == '.' && apr_isdigit(r->protocol[7])
+                 && r->protocol[5] != '0') {
+        r->assbackwards = 0;
+        r->proto_num = HTTP_VERSION(r->protocol[5] - '0', r->protocol[7] - '0');
+        if (strict && deferred_error == rrl_none)
+            deferred_error = rrl_badprotocol;
+        else
+            memcpy((char*)r->protocol, "HTTP", 4);
+    }
+    else if (r->protocol[0]) {
+        r->assbackwards = 0;
+        r->proto_num = HTTP_VERSION(1,0);
+        /* Defer setting the r->protocol string till error msg is composed */
+        if (strict && deferred_error == rrl_none)
+            deferred_error = rrl_badprotocol;
+        else
+            r->protocol  = "HTTP/1.0";
+    }
+    else {
+        r->assbackwards = 1;
+        r->protocol = "HTTP/0.9";
+        r->proto_num = HTTP_VERSION(0, 9);
+    }
 
+    /* Satisfy the method_number and uri fields prior to invoking error
+     * handling, such that these fields are available for subsitution
+     */
     r->method_number = ap_method_number_of(r->method);
-    if (r->method_number == M_GET && r->method[0] == 'H') {
+    if (r->method_number == M_GET && r->method[0] == 'H')
         r->header_only = 1;
-    }
 
     ap_parse_uri(r, uri);
-    if (r->status != HTTP_OK) {
-        r->proto_num = HTTP_VERSION(1,0);
-        r->protocol  = apr_pstrdup(r->pool, "HTTP/1.0");
-        return 0;
-    }
 
-    if (ll[0]) {
-        r->assbackwards = 0;
-        pro = ll;
-        len = strlen(ll);
+    if (r->proto_num == HTTP_VERSION(0, 9)) {
+        if (conf->http09_enable == AP_HTTP09_DISABLE
+                && deferred_error == rrl_none) {
+            /* If we deny 0.9, send error message with 1.x behavior */
+            r->assbackwards = 0;
+            r->connection->keepalive = AP_CONN_CLOSE;
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02401)
+                          "HTTP Request Line; Rejected HTTP/0.9 request");
+            r->status = HTTP_VERSION_NOT_SUPPORTED;
+            return 0;
+        }
+        if (strict && strcmp(r->method, "GET") && deferred_error == rrl_none) {
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(03444)
+                          "HTTP Request Line; Invalid method token: '%.*s'"
+                          " (only GET is allowed for HTTP/0.9 requests)",
+                          field_name_len(r->method), r->method);
+            r->status = HTTP_BAD_REQUEST;
+            return 0;
+        }
     }
-    else {
-        r->assbackwards = 1;
-        pro = "HTTP/0.9";
-        len = 8;
-        if (conf->http09_enable == AP_HTTP09_DISABLE) {
-                r->status = HTTP_VERSION_NOT_SUPPORTED;
-                r->protocol = apr_pstrmemdup(r->pool, pro, len);
-                /* If we deny 0.9, send error message with 1.x */
-                r->assbackwards = 0;
-                r->proto_num = HTTP_VERSION(0, 9);
-                r->connection->keepalive = AP_CONN_CLOSE;
-                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02401)
-                              "HTTP/0.9 denied by server configuration");
-                return 0;
+
+    if (deferred_error != rrl_none) {
+        if (deferred_error == rrl_badmethod)
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(03445)
+                          "HTTP Request Line; Invalid method token: '%.*s'",
+                          field_name_len(r->method), r->method);
+        else if (deferred_error == rrl_missinguri)
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(03446)
+                          "HTTP Request Line; Missing URI");
+        else if (deferred_error == rrl_badwhitespace)
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(03447)
+                          "HTTP Request Line; Invalid whitespace");
+        else if (deferred_error == rrl_excesswhitespace)
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(03448)
+                          "HTTP Request Line; Inappropriate whitespace "
+                          "(disallowed by StrictWhitespace");
+        else if (deferred_error == rrl_trailingtext)
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(03449)
+                          "HTTP Request Line; Extraneous text found '%.*s' "
+                          "(perhaps whitespace was injected?)",
+                          field_name_len(ll), ll);
+        else if (deferred_error == rrl_badprotocol) {
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02418)
+                          "HTTP Request Line; Unrecognized protocol '%.*s' "
+                          "(perhaps whitespace was injected?)",
+                          field_name_len(r->protocol), r->protocol);
+            r->protocol  = "HTTP/1.0";
         }
+        r->status = HTTP_BAD_REQUEST;
+        return 0;
     }
-    r->protocol = apr_pstrmemdup(r->pool, pro, len);
 
-    /* Avoid sscanf in the common case */
-    if (len == 8
-        && pro[0] == 'H' && pro[1] == 'T' && pro[2] == 'T' && pro[3] == 'P'
-        && pro[4] == '/' && apr_isdigit(pro[5]) && pro[6] == '.'
-        && apr_isdigit(pro[7])) {
-        r->proto_num = HTTP_VERSION(pro[5] - '0', pro[7] - '0');
+    if (conf->http_methods == AP_HTTP_METHODS_REGISTERED
+            && r->method_number == M_INVALID) {
+        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02423)
+                      "HTTP Request Line; Unrecognized HTTP method: '%.*s' "
+                      "(disallowed by RegisteredMethods)",
+                      field_name_len(r->method), r->method);
+        r->status = HTTP_NOT_IMPLEMENTED;
+        return 0;
     }
-    else {
-        if (strict) {
-            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02418)
-                          "Invalid protocol '%s'", r->protocol);
-            if (enforce_strict) {
-                r->proto_num = HTTP_VERSION(1,0);
-                r->protocol  = apr_pstrdup(r->pool, "HTTP/1.0");
-                r->connection->keepalive = AP_CONN_CLOSE;
-                r->status = HTTP_BAD_REQUEST;
-                return 0;
-            }
-        }
-        if (3 == sscanf(r->protocol, "%4s/%u.%u", http, &major, &minor)
-            && (strcasecmp("http", http) == 0)
-            && (minor < HTTP_VERSION(1, 0)) ) { /* don't allow HTTP/0.1000 */
-            r->proto_num = HTTP_VERSION(major, minor);
-        }
-        else {
-            r->proto_num = HTTP_VERSION(1, 0);
-        }
+
+    if (r->status != HTTP_OK) {
+        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(03450)
+                      "HTTP Request Line; URI parsing failed");
+        return 0;
     }
 
     if (strict) {
-        int err = 0;
         if (ap_has_cntrl(r->the_request)) {
-            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02420)
-                          "Request line must not contain control characters");
-            err = HTTP_BAD_REQUEST;
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02420)
+                          "HTTP Request Line; URI must not contain control"
+                          " characters");
+            r->status = HTTP_BAD_REQUEST;
+            return 0;
         }
         if (r->parsed_uri.fragment) {
             /* RFC3986 3.5: no fragment */
-            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02421)
-                          "URI must not contain a fragment");
-            err = HTTP_BAD_REQUEST;
-        }
-        else if (r->parsed_uri.user || r->parsed_uri.password) {
-            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02422)
-                          "URI must not contain a username/password");
-            err = HTTP_BAD_REQUEST;
-        }
-        else if (r->method_number == M_INVALID) {
-            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02423)
-                          "Invalid HTTP method string: %s", r->method);
-            err = HTTP_NOT_IMPLEMENTED;
-        }
-        else if (r->assbackwards == 0 && r->proto_num < HTTP_VERSION(1, 0)) {
-            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02424)
-                          "HTTP/0.x does not take a protocol");
-            err = HTTP_BAD_REQUEST;
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02421)
+                          "HTTP Request Line; URI must not contain a fragment");
+            r->status = HTTP_BAD_REQUEST;
+            return 0;
         }
-
-        if (err && enforce_strict) {
-            r->status = err;
+        if (r->parsed_uri.user || r->parsed_uri.password) {
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02422)
+                          "HTTP Request Line; URI must not contain a "
+                          "username/password");
+            r->status = HTTP_BAD_REQUEST;
             return 0;
         }
     }
@@ -743,16 +853,6 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
     return 1;
 }
 
-/* get the length of the field name for logging, but no more than 80 bytes */
-#define LOG_NAME_MAX_LEN 80
-static int field_name_len(const char *field)
-{
-    const char *end = ap_strchr_c(field, ':');
-    if (end == NULL || end - field > LOG_NAME_MAX_LEN)
-        return LOG_NAME_MAX_LEN;
-    return end - field;
-}
-
 static int table_do_fn_check_lengths(void *r_, const char *key,
                                      const char *value)
 {
@@ -780,6 +880,7 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb
     int fields_read = 0;
     char *tmp_field;
     core_server_config *conf = ap_get_core_module_config(r->server->module_config);
+    int strict = (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE);
 
     /*
      * Read header lines until we get the empty separator line, a read error,
@@ -892,7 +993,7 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb
             }
             memcpy(last_field + last_len, field, len +1); /* +1 for nul */
             /* Replace obs-fold w/ SP per RFC 7230 3.2.4 */
-            if (conf->http_conformance & AP_HTTP_CONFORMANCE_STRICT) {
+            if (strict) {
                 last_field[last_len] = ' ';
             }
             last_len += len;
@@ -921,9 +1022,8 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb
                 return;
             }
 
-            if (!(conf->http_conformance & AP_HTTP_CONFORMANCE_STRICT))
-            {
-                /* Not Strict, using the legacy parser */
+            if (!strict) {
+                /* Not Strict ('Unsafe' mode), using the legacy parser */
 
                 if (!(value = strchr(last_field, ':'))) { /* Find ':' or */
                     r->status = HTTP_BAD_REQUEST;   /* abort bad request */
index 393e7ab4a814ac915b70fcd04f45c1a3451468a0..ebf1a995aa84789abf3e587439290a71d457bcc8 100644 (file)
@@ -751,8 +751,7 @@ static apr_status_t fix_hostname_non_v6(request_rec *r, char *host)
  * If strict mode ever becomes the default, this should be folded into
  * fix_hostname_non_v6()
  */
-static apr_status_t strict_hostname_check(request_rec *r, char *host,
-                                          int logonly)
+static apr_status_t strict_hostname_check(request_rec *r, char *host)
 {
     char *ch;
     int is_dotted_decimal = 1, leading_zeroes = 0, dots = 0;
@@ -795,8 +794,6 @@ bad:
     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02415)
                   "[strict] Invalid host name '%s'%s%.6s",
                   host, *ch ? ", problem near: " : "", ch);
-    if (logonly)
-        return APR_SUCCESS;
     return APR_EINVAL;
 }
 
@@ -822,8 +819,7 @@ static int fix_hostname(request_rec *r, const char *host_header,
     apr_status_t rv;
     const char *c;
     int is_v6literal = 0;
-    int strict = http_conformance & AP_HTTP_CONFORMANCE_STRICT;
-    int strict_logonly = http_conformance & AP_HTTP_CONFORMANCE_LOGONLY;
+    int strict = (http_conformance != AP_HTTP_CONFORMANCE_UNSAFE);
 
     src = host_header ? host_header : r->hostname;
 
@@ -843,8 +839,7 @@ static int fix_hostname(request_rec *r, const char *host_header,
             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02416)
                          "[strict] purely numeric host names not allowed: %s",
                          src);
-            if (!strict_logonly)
-                goto bad_nolog;
+            goto bad_nolog;
         }
         r->hostname = src;
         return is_v6literal;
@@ -882,7 +877,7 @@ static int fix_hostname(request_rec *r, const char *host_header,
     else {
         rv = fix_hostname_non_v6(r, host);
         if (strict && rv == APR_SUCCESS)
-            rv = strict_hostname_check(r, host, strict_logonly);
+            rv = strict_hostname_check(r, host);
     }
     if (rv != APR_SUCCESS)
         goto bad;
@@ -1162,7 +1157,7 @@ AP_DECLARE(void) ap_update_vhost_from_headers(request_rec *r)
     if (r->status != HTTP_OK)
         return;
 
-    if (conf->http_conformance & AP_HTTP_CONFORMANCE_STRICT) {
+    if (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE) {
         /*
          * If we have both hostname from an absoluteURI and a Host header,
          * we must ignore the Host header (RFC 2616 5.2).
@@ -1172,10 +1167,8 @@ AP_DECLARE(void) ap_update_vhost_from_headers(request_rec *r)
         if (have_hostname_from_url && host_header != NULL) {
             const char *info = "Would replace";
             const char *new = construct_host_header(r, is_v6literal);
-            if (!(conf->http_conformance & AP_HTTP_CONFORMANCE_LOGONLY)) {
-                apr_table_set(r->headers_in, "Host", r->hostname);
-                info = "Replacing";
-            }
+            apr_table_set(r->headers_in, "Host", r->hostname);
+            info = "Replacing";
             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02417)
                           "%s Host header '%s' with host from request uri: "
                           "'%s'", info, host_header, new);