]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
Fix syntax
authorWilliam A. Rowe Jr <wrowe@apache.org>
Mon, 14 Nov 2016 17:59:10 +0000 (17:59 +0000)
committerWilliam A. Rowe Jr <wrowe@apache.org>
Mon, 14 Nov 2016 17:59:10 +0000 (17:59 +0000)
Submitted by: jailletc36
Backport: r1756862

Introduce StrictURI|UnsafeURI for RFC3986 enforcement
Submitted by: wrowe
Backport: r1756959

Surpress noise about syntax
Submitted by: wrowe
Backport: r1756978

Yann is correct, % is distinct from reserved and unreserved
Submitted by: wrowe
Backport: r1757062

As commented, ensure we don't flag a request as a rejected 0.9 request
if we identified any other parsing errors and handle all 0.9 request
errors as 400 BAD REQUEST, presuming HTTP/1.0 to deliver the error details.
Do not report 0.9 issues as 505 INVALID PROTOCOL because the client apparently
specified no protocol, and 505 post-dates the simple HTTP request mechanism.
Submitted by: wrowe
Backport: r1757065

Rename LenientWhitespace to UnsafeWhitespace and change StrictWhitespace
to the default behavior, after discussion with fielding et al about the
purpose of section 3.5. Update the documentation to clarify this.

This patch removes whitespace considerations from the Strict|Unsafe toggle
and consolidates them all in the StrictWhitespace|UnsafeWhitespace toggle.

Added a bunch of logic comments to read_request_line parsing.

Dropped the badwhitespace list for an all-or-nothing toggle in rrl.

Leading space before the method is optimized to be evaluated only once.

Toggled the request from HTTP/0.9 to HTTP/1.0 for more BAD_REQUEST cases.

Moved s/[\n\v\f\r]/ / cleanup logic earlier in the cycle, to operate on
each individual line read, and catch bad whitespace errors earlier.
This changes the obs-fold to more efficiently condense whitespace and
forces concatinatination with a single SP, always. Overrides are not
necessary since obs-fold is clearly deprecated.
Submitted by: wrowe
Backport: r1757589

Also catch invalid spaces between the URI <> Protocol in StrictWhitespace mode.
(matching the test for the Method <> URI)
Submitted by: wrowe
Backport: r1757593

Correct RFC reference text (link was right)
Submitted by: wrowe
Backport: r1757711

First survey results, all intrinsicly bad input will be logged at the debug
level, no louder. This patch intentionally dodges the Limit* constrained tests
since administrators may shoot themselves in the foot, or be confronted with
impossibly long cookie values, etc.

Adjust the documentation to match.
Submitted by: wrowe
Backport: r1757920

Correct URL failure reporting.

Drop the second reporting of HEAD over HTTP/0.9 requests, we short-circuit
this early now in read_request_line() when presented anything other than
the sole "GET" method permitted by spec.
Revert to the correct APLOGNO ID for this case
Submitted by: wrowe
Backport: r1757921, r1757924

Folding StrictWhitespace into the Strict ruleset of RFC7230, per dev@ poll.
This choice is unanimous, although StrictURI (a different RFC) still hasn't
found absolute concensus.
Submitted by: wrowe
Backport: r1758226

Correct the parser construction for several optimizations,
based on the fact that bad whitespace shall not be permitted
or corrected in any operating mode, while preserving the
ability to extract bad method/uri/proto for later reporting
and diagnostics.

This change causes badwhitespace in the request line or any
request field line to always fail, and not honor the setting
of the HttpProtocolOptions Unsafe option. Mult SP characters
or trailing SP characters in the request line are still
permitted in Unsafe mode.

Adjusted several error message emits to match these changes.
Submitted by: wrowe
Backport: r1758263

Clarify documentation based on concensus decisions discussed on dev@
and reflecting the current implementation, clean up stray <p>
Submitted by: wrowe
Backport: r1758265, r1758266

New optional flag to enforce <CR><LF> line delimiters in ap_[r]getline,
created by overloading 'int fold' (1 or 0) as 'int flags', with the same
value 1 for AP_GETLINE_FOLD (which httpd doesn't use), and a new value
2 for AP_GETLINE_CRLF

Enforce CRLF when HttpProtocolOptions Strict is in force.

Correctly introduces a new t/TEST fail.
Submitted by: wrowe
Backport: r1758304

Calm some overly agressive crlf handling, and clarify
Submitted by: wrowe
Backport: r1758305, r1758313

Review of IE 11, Firefox 48 and Chrome 53 all indicate that ';' URI characters
are transmitted unencoded, per RFC3986 section 3.3 grammer. Correct httpd's
behavior to not encode ';' in proxied URI's or Location: response headers.
Submitted by: wrowe
Backport: r1760444

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

CHANGES
docs/manual/mod/core.xml
include/ap_mmn.h
include/http_core.h
include/http_protocol.h
include/httpd.h
server/core.c
server/gen_test_char.c
server/protocol.c
server/util.c

diff --git a/CHANGES b/CHANGES
index 015143568617c928dca7bef8b7f4464f0cf43b72..3bfbae6ce72ee75f9cae0839565328367227b7a0 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,10 @@
 
 Changes with Apache 2.4.24
 
+  *) core: Permit unencoded ';' characters to appear in proxy requests and
+     Location: response headers. Corresponds to modern browser behavior.
+     [William Rowe]
+
   *) core: ap_rgetline_core now pulls from r->proto_input_filters.
 
   *) core, http: Extend HttpProtocol with an option to enforce stricter HTTP
index 066bb845030932668bcef841104c8cac7ccc3f34..3c68b5922c95e1ae32c768426ce1b349812cbc1b 100644 (file)
@@ -1239,12 +1239,11 @@ EnableSendfile On
 </directivesynopsis>
 
 <directivesynopsis>
-<name>HTTPProtocolOptions</name>
+<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>
+<syntax>HttpProtocolOptions [Strict|Unsafe] [StrictURL|UnsafeURL]
+ [RegisteredMethods|LenientMethods] [Allow0.9|Require1.0]</syntax>
+<default>HttpProtocolOptions Strict StrictURL LenientMethods Allow0.9</default>
 <contextlist><context>server config</context>
 <context>virtual host</context></contextlist>
 <compatibility>2.2.32 or 2.4.24 and later</compatibility>
@@ -1256,11 +1255,11 @@ LenientMethods</default>
     (<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>
+    custom user-agents which must be deperecated, <code>Unsafe</code>
+    and <code>UnsafeURL</code> options have been added to revert to the legacy
+    behaviors. These rules are applied prior to request processing, so must be
+    configured at the global or default (first) matching virtual host section,
+    by IP/port 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
@@ -1269,53 +1268,58 @@ LenientMethods</default>
       >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>
-
+    risks of accepting non-conformant request messages, while
+    <a href="https://tools.ietf.org/html/rfc7230#section-3.5"
+         >RFC 7230 &sect;3.5</a> "Message Parsing Robustness" identify the
+    risks of accepting obscure whitespace and request message formatting. 
+    As of the introduction of this directive, all grammer rules of the
+    specification are enforced in the default <code>Strict</code> operating
+    mode, and the strict whitespace suggested by section 3.5 is enforced
+    and cannot be relaxed.</p>
+
+    <p><a href="https://tools.ietf.org/html/rfc3986#section-2.2"
+         >RFC 3986 &sect;2.2 and 2.3</a> define "Reserved Characters" and
+    "Unreserved Characters". All other character octets are required to
+    be %XX encoded under this spec, and RFC7230 defers to these requirements.
+    By default the <code>StrictURI</code> option will reject all requests 
+    containing invalid characters. This rule can be relaxed with the
+    <code>UnsafeURI</code> option to support badly written user-agents.</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,
+    or <code>UnsafeURI</code> modes of operation, particularly on
+    outward-facing, publicly accessible server deployments.
+    If an interface is required for faulty monitoring or other custom service
+    consumers running on an intranet, users should toggle only those Unsafe
+    options which are necessary, and only on a specific virtual host configured
+    to service only their internal private network.</p>
+
+    <p>Reviewing the messages logged to the <directive>ErrorLog</directive>,
+    configured with <directive>LogLevel</directive> <code>debug</code> level,
     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>
+    Users should pay particular attention to the 400 responses in the access
+    log for invalid requests which were unexpectedly rejected.</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>
+    to toggle the <code>RegisteredMethods</code> option and register any
+    non-standard methods 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>
+
+    <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 7230 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>
 </usage>
 </directivesynopsis>
 
index 7e1c19c3a74b427e8164e7956999449654239493..01267ff146f74f2aa57edd965d930468e28b43d5 100644 (file)
  * 20120211.65 (2.4.24-dev) Add ap_check_pipeline().
  * 20120211.66 (2.4.24-dev) Rename ap_proxy_check_backend() to
  *                          ap_proxy_check_connection().
- * 20120211.67 (2.5.0-dev)  Add http09_enable to core_server_config
- *                          Add http_conformance to core_server_config
+ * 20120211.67 (2.4.24-dev) Add http09_enable, http_conformance, and
+ *                          http_methods 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
+ *                          Replaced fold boolean with with multiple bit flags
+ *                          to ap_[r]getline()
  */
 
 #define MODULE_MAGIC_COOKIE 0x41503234UL /* "AP24" */
index 397f89137af4363c768b710fe4ca4f610b7096d2..836e814717690c1ed086055ba4ed85fec6ce0c21 100644 (file)
@@ -736,16 +736,16 @@ typedef struct {
 #define AP_HTTP_CONFORMANCE_STRICT    2
     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;
 
+#define AP_HTTP_URI_UNSET             0
+#define AP_HTTP_URI_UNSAFE            1
+#define AP_HTTP_URI_STRICT            2
+    char http_stricturi;
+
 } core_server_config;
 
 /* for AddOutputFiltersByType in core.c */
index 447e3a8c0308931e56cdb1298bf17a3d434f854a..a9e09904bdc32bd6ca853eafd6f5c7868f1a4997 100644 (file)
@@ -582,17 +582,22 @@ AP_DECLARE(int) ap_get_basic_auth_pw(request_rec *r, const char **pw);
  */
 AP_CORE_DECLARE(void) ap_parse_uri(request_rec *r, const char *uri);
 
+#define AP_GETLINE_FOLD 1 /* Whether to merge continuation lines */
+#define AP_GETLINE_CRLF 2 /*Whether line ends must be in the form CR LF */
+
 /**
  * Get the next line of input for the request
  * @param s The buffer into which to read the line
  * @param n The size of the buffer
  * @param r The request
- * @param fold Whether to merge continuation lines
+ * @param flags Bit flag of multiple parsing options
+ *              AP_GETLINE_FOLD Whether to merge continuation lines
+ *              AP_GETLINE_CRLF Whether line ends must be in the form CR LF
  * @return The length of the line, if successful
  *         n, if the line is too big to fit in the buffer
  *         -1 for miscellaneous errors
  */
-AP_DECLARE(int) ap_getline(char *s, int n, request_rec *r, int fold);
+AP_DECLARE(int) ap_getline(char *s, int n, request_rec *r, int flags);
 
 /**
  * Get the next line of input for the request
@@ -610,7 +615,9 @@ AP_DECLARE(int) ap_getline(char *s, int n, request_rec *r, int fold);
  * @param n The size of the buffer
  * @param read The length of the line.
  * @param r The request
- * @param fold Whether to merge continuation lines
+ * @param flags Bit flag of multiple parsing options
+ *              AP_GETLINE_FOLD Whether to merge continuation lines
+ *              AP_GETLINE_CRLF Whether line ends must be in the form CR LF
  * @param bb Working brigade to use when reading buckets
  * @return APR_SUCCESS, if successful
  *         APR_ENOSPC, if the line is too big to fit in the buffer
@@ -619,7 +626,7 @@ AP_DECLARE(int) ap_getline(char *s, int n, request_rec *r, int fold);
 #if APR_CHARSET_EBCDIC
 AP_DECLARE(apr_status_t) ap_rgetline(char **s, apr_size_t n,
                                      apr_size_t *read,
-                                     request_rec *r, int fold,
+                                     request_rec *r, int flags,
                                      apr_bucket_brigade *bb);
 #else /* ASCII box */
 #define ap_rgetline(s, n, read, r, fold, bb) \
@@ -629,7 +636,7 @@ AP_DECLARE(apr_status_t) ap_rgetline(char **s, apr_size_t n,
 /** @see ap_rgetline */
 AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
                                           apr_size_t *read,
-                                          request_rec *r, int fold,
+                                          request_rec *r, int flags,
                                           apr_bucket_brigade *bb);
 
 /**
index 592e54188a3162d72cf23d9a61eb7554687f6102..e2d4a4e2c54e72ce49d27d375beb016629c33fcc 100644 (file)
@@ -1600,6 +1600,13 @@ AP_DECLARE(const char *) ap_scan_http_field_content(const char *ptr);
  */
 AP_DECLARE(const char *) ap_scan_http_token(const char *ptr);
 
+/* Scan a string for valid URI characters per RFC3986, and 
+ * return a pointer to the first non-URI character encountered.
+ * @param ptr The string to scan
+ * @return A pointer to the first non-token character.
+ */
+AP_DECLARE(const char *) ap_scan_http_uri_safe(const char *ptr);
+
 /* Retrieve a token, advancing the pointer to the first non-token character
  * and returning a copy of the token string.
  * @param ptr The string to scan. On return, this points to the first non-token
index d988ad574292994f4bf3141c83b67333a3668859..01a0a936ff3d557ff91b60170ad88680cbda8b70 100644 (file)
@@ -525,8 +525,8 @@ 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_stricturi != AP_HTTP_URI_UNSET)
+        conf->http_stricturi = virt->http_stricturi;
 
     if (virt->http_methods != AP_HTTP_METHODS_UNSET)
         conf->http_methods = virt->http_methods;
@@ -3921,35 +3921,36 @@ static const char *set_http_protocol_options(cmd_parms *cmd, void *dummy,
         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, "stricturi") == 0)
+        conf->http_stricturi |= AP_HTTP_URI_STRICT;
+    else if (strcasecmp(arg, "unsafeuri") == 0)
+        conf->http_stricturi |= AP_HTTP_URI_UNSAFE;
     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 "HttpProtocolOptions accepts 'Allow0.9' (default) or "
-               "'Require1.0', 'Unsafe' or 'Strict' (default), "
-               "'StrictWhitespace' or 'LenientWhitespace (default), and "
-               "'RegisteredMethods' or 'LenientMethods (default)";
+        return "HttpProtocolOptions accepts "
+               "'Unsafe' or 'Strict' (default), "
+               "'UnsafeURI' or 'StrictURI' (default), "
+               "'RegisteredMethods' or 'LenientMethods' (default), and "
+               "'Require1.0' or 'Allow0.9' (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_stricturi & AP_HTTP_URI_STRICT)
+            && (conf->http_stricturi & AP_HTTP_URI_UNSAFE))
+        return "HttpProtocolOptions 'StrictURI' and 'UnsafeURI'"
+               " 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'"
index 046f47b51bbd7ac41c986381fc549eafa1fae6dc..251e7fb2e32f4a69f535608e054290d82bb84a51 100644 (file)
 #define T_ESCAPE_FORENSIC     (0x20)
 #define T_ESCAPE_URLENCODED   (0x40)
 #define T_HTTP_CTRLS          (0x80)
+#define T_URI_RFC3986        (0x100)
 
 int main(int argc, char *argv[])
 {
     unsigned c;
-    unsigned char flags;
+    unsigned short flags;
 
     printf("/* this file is automatically generated by gen_test_char, "
            "do not edit */\n"
@@ -69,8 +70,9 @@ int main(int argc, char *argv[])
            "#define T_ESCAPE_FORENSIC      (%u)\n"
            "#define T_ESCAPE_URLENCODED    (%u)\n"
            "#define T_HTTP_CTRLS           (%u)\n"
+           "#define T_URI_RFC3986          (%u)\n"
            "\n"
-           "static const unsigned char test_char_table[256] = {",
+           "static const unsigned short test_char_table[256] = {",
            T_ESCAPE_SHELL_CMD,
            T_ESCAPE_PATH_SEGMENT,
            T_OS_ESCAPE_PATH,
@@ -78,7 +80,8 @@ int main(int argc, char *argv[])
            T_ESCAPE_LOGITEM,
            T_ESCAPE_FORENSIC,
            T_ESCAPE_URLENCODED,
-           T_HTTP_CTRLS);
+           T_HTTP_CTRLS,
+           T_URI_RFC3986);
 
     for (c = 0; c < 256; ++c) {
         flags = 0;
@@ -110,7 +113,7 @@ int main(int argc, char *argv[])
             flags |= T_ESCAPE_PATH_SEGMENT;
         }
 
-        if (!apr_isalnum(c) && !strchr("$-_.+!*'(),:@&=/~", c)) {
+        if (!apr_isalnum(c) && !strchr("$-_.+!*'(),:;@&=/~", c)) {
             flags |= T_OS_ESCAPE_PATH;
         }
 
@@ -122,7 +125,7 @@ int main(int argc, char *argv[])
          * and "tspecials" (RFC2068) a.k.a. "separators" (RFC2616), which
          * is easer to express as characters remaining in the ASCII token set
          */
-        if (!(apr_isalnum(c) || strchr("!#$%&'*+-.^_`|~", c))) {
+        if (!c || !(apr_isalnum(c) || strchr("!#$%&'*+-.^_`|~", c))) {
             flags |= T_HTTP_TOKEN_STOP;
         }
 
@@ -136,6 +139,17 @@ int main(int argc, char *argv[])
             flags |= T_HTTP_CTRLS;
         }
 
+        /* From RFC3986, the specific sets of gen-delims, sub-delims (2.2),
+         * and unreserved (2.3) that are possible somewhere within a URI.
+         * Spec requires all others to be %XX encoded, including obs-text.
+         */
+        if (c && (strchr("%"                              /* pct-encode */
+                         ":/?#[]@"                        /* gen-delims */ 
+                         "!$&'()*+,;="                    /* sub-delims */
+                         "-._~", c) || apr_isalnum(c))) { /* unreserved */
+            flags |= T_URI_RFC3986;
+        }
+
         /* For logging, escape all control characters,
          * double quotes (because they delimit the request in the log file)
          * backslashes (because we use backslash for escaping)
@@ -153,7 +167,7 @@ int main(int argc, char *argv[])
             flags |= T_ESCAPE_FORENSIC;
         }
 
-        printf("0x%02x%c", flags, (c < 255) ? ',' : ' ');
+        printf("0x%03x%c", flags, (c < 255) ? ',' : ' ');
     }
 
     printf("\n};\n");
index b78445545715d60c65157b4cd917c6d5330bf2d0..0dd66cae5ef32064ac3bb8d3d5cc5a271293a141 100644 (file)
@@ -204,7 +204,7 @@ AP_DECLARE(apr_time_t) ap_rationalize_mtime(request_rec *r, apr_time_t mtime)
  * APR_ENOSPC is returned if there is not enough buffer space.
  * Other errors may be returned on other errors.
  *
- * The LF is *not* returned in the buffer.  Therefore, a *read of 0
+ * The [CR]LF are *not* returned in the buffer.  Therefore, a *read of 0
  * indicates that an empty line was read.
  *
  * Notes: Because the buffer uses 1 char for NUL, the most we can return is
@@ -215,13 +215,15 @@ AP_DECLARE(apr_time_t) ap_rationalize_mtime(request_rec *r, apr_time_t mtime)
  */
 AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
                                           apr_size_t *read, request_rec *r,
-                                          int fold, apr_bucket_brigade *bb)
+                                          int flags, apr_bucket_brigade *bb)
 {
     apr_status_t rv;
     apr_bucket *e;
     apr_size_t bytes_handled = 0, current_alloc = 0;
     char *pos, *last_char = *s;
     int do_alloc = (*s == NULL), saw_eos = 0;
+    int fold = flags & AP_GETLINE_FOLD;
+    int crlf = flags & AP_GETLINE_CRLF;
 
     /*
      * Initialize last_char as otherwise a random value will be compared
@@ -327,6 +329,13 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
         }
     }
 
+    if (crlf && (last_char <= *s || last_char[-1] != APR_ASCII_CR)) {
+        *last_char = '\0';
+        bytes_handled = last_char - *s;
+        *read = bytes_handled;
+        return APR_EINVAL;
+    }
+
     /* Now NUL-terminate the string at the end of the line;
      * if the last-but-one character is a CR, terminate there */
     if (last_char > *s && last_char[-1] == APR_ASCII_CR) {
@@ -471,7 +480,7 @@ AP_DECLARE(apr_status_t) ap_rgetline(char **s, apr_size_t n,
 }
 #endif
 
-AP_DECLARE(int) ap_getline(char *s, int n, request_rec *r, int fold)
+AP_DECLARE(int) ap_getline(char *s, int n, request_rec *r, int flags)
 {
     char *tmp_s = s;
     apr_status_t rv;
@@ -479,7 +488,7 @@ AP_DECLARE(int) ap_getline(char *s, int n, request_rec *r, int fold)
     apr_bucket_brigade *tmp_bb;
 
     tmp_bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
-    rv = ap_rgetline(&tmp_s, n, &len, r, fold, tmp_bb);
+    rv = ap_rgetline(&tmp_s, n, &len, r, flags, tmp_bb);
     apr_brigade_destroy(tmp_bb);
 
     /* Map the out-of-space condition to the old API. */
@@ -573,7 +582,8 @@ 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
+        rrl_missinguri, rrl_baduri, rrl_badprotocol, rrl_trailingtext,
+        rrl_badmethod09, rrl_reject09
     } deferred_error = rrl_none;
     char *ll;
     char *uri;
@@ -581,8 +591,7 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
     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_UNSAFE);
-    const char *badwhitespace = strict ? "\t\n\v\f\r" : "\n\v\f\r";
-    int strictspaces = (conf->http_whitespace == AP_HTTP_WHITESPACE_STRICT);
+    int stricturi = (conf->http_stricturi != AP_HTTP_URI_UNSAFE);
 
     /* Read past empty lines until we get a real request line,
      * a read error, the connection closes (EOF), or we timeout.
@@ -607,7 +616,7 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
          */
         r->the_request = NULL;
         rv = ap_rgetline(&(r->the_request), (apr_size_t)(r->server->limit_req_line + 2),
-                         &len, r, 0, bb);
+                         &len, r, strict ? AP_GETLINE_CRLF : 0, bb);
 
         if (rv != APR_SUCCESS) {
             r->request_time = apr_time_now();
@@ -640,14 +649,21 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
     r->request_time = apr_time_now();
 
     r->method = r->the_request;
-    if (apr_isspace(*r->method))
-        deferred_error = rrl_excesswhitespace; 
-    for ( ; apr_isspace(*r->method); ++r->method)
-        ; 
 
+    /* If there is whitespace before a method, skip it and mark in error */
+    if (apr_isspace(*r->method)) {
+        deferred_error = rrl_badwhitespace; 
+        for ( ; apr_isspace(*r->method); ++r->method)
+            ; 
+    }
+
+    /* Scan the method up to the next whitespace, ensure it contains only
+     * valid http-token characters, otherwise mark in error
+     */
     if (strict) {
         ll = (char*) ap_scan_http_token(r->method);
-        if ((ll == r->method) || (*ll && !apr_isspace(*ll))) {
+        if (((ll == r->method) || (*ll && !apr_isspace(*ll)))
+                && deferred_error == rrl_none) {
             deferred_error = rrl_badmethod;
             ll = strpbrk(ll, "\t\n\v\f\r ");
         }
@@ -655,6 +671,8 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
     else {
         ll = strpbrk(r->method, "\t\n\v\f\r ");
     }
+
+    /* Verify method terminated with a single SP, or mark as specific error */
     if (!ll) {
         if (deferred_error == rrl_none)
             deferred_error = rrl_missinguri;
@@ -662,47 +680,84 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
         len = 0;
         goto rrl_done;
     }
-
-    if (strictspaces && ll[0] && (ll[0] != ' ' || apr_isspace(ll[1]))
-            && deferred_error == rrl_none) {
+    else if (strict && ll[0] && (ll[0] != ' ' || apr_isspace(ll[1]))
+                 && deferred_error == rrl_none) {
         deferred_error = rrl_excesswhitespace; 
     }
+
+    /* 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 (ap_strchr_c(badwhitespace, *uri) && deferred_error == rrl_none)
+        if (ap_strchr_c("\t\n\v\f\r", *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"))) {
+    /* Scan the URI up to the next whitespace, ensure it contains only
+     * valid RFC3986 characters, otherwise mark in error
+     */
+    if (stricturi) {
+        ll = (char*) ap_scan_http_uri_safe(uri);
+        if (ll == uri || (*ll && !apr_isspace(*ll))) {
+            deferred_error = rrl_baduri;
+            ll = strpbrk(ll, "\t\n\v\f\r ");
+        }
+    }
+    else {
+        ll = strpbrk(uri, "\t\n\v\f\r ");
+    }
+
+    /* Verify method terminated with a single SP, or mark as specific error */
+    if (!ll) {
         r->protocol = "";
         len = 0;
         goto rrl_done;
     }
+    else if (strict && ll[0] && (ll[0] != ' ' || apr_isspace(ll[1]))
+            && deferred_error == rrl_none) {
+        deferred_error = rrl_excesswhitespace; 
+    }
+
+    /* Advance protocol pointer over leading whitespace, NUL terminate the uri
+     * If non-SP whitespace is encountered, mark as specific error
+     */
     for (r->protocol = ll; apr_isspace(*r->protocol); ++r->protocol) 
-        if (ap_strchr_c(badwhitespace, *r->protocol) && deferred_error == rrl_none)
+        if (ap_strchr_c("\t\n\v\f\r", *r->protocol)
+                && deferred_error == rrl_none)
             deferred_error = rrl_badwhitespace; 
     *ll = '\0';
+
+    /* Scan the protocol up to the next whitespace, validation comes later */
     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 (ap_strchr_c(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';
+
+    /* Advance over trailing whitespace, if found mark in error,
+     * determine if trailing text is found, unconditionally mark in error,
+     * finally NUL terminate the protocol string
+     */
+    if (strict && *ll) {
+        deferred_error = rrl_excesswhitespace;
+    }
+    else {
+        for ( ; apr_isspace(*ll); ++ll)
+            if (ap_strchr_c("\t\n\v\f\r", *ll)
+                    && deferred_error == rrl_none)
+                deferred_error = rrl_badwhitespace; 
+        if (*ll && deferred_error == rrl_none)
+            deferred_error = rrl_trailingtext;
+    }
+    *((char *)r->protocol + len) = '\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
+    /* For internal integrety 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->protocol ? " " : NULL, r->protocol, NULL);
@@ -746,7 +801,7 @@ rrl_done:
         r->proto_num = HTTP_VERSION(0, 9);
     }
 
-    /* Satisfy the method_number and uri fields prior to invoking error
+    /* Determine the method_number and parse the uri prior to invoking error
      * handling, such that these fields are available for subsitution
      */
     r->method_number = ap_method_number_of(r->method);
@@ -755,99 +810,118 @@ rrl_done:
 
     ap_parse_uri(r, uri);
 
-    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;
-        }
+    /* 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)
+            deferred_error = rrl_reject09;
+        else if (strict && (r->method_number != M_GET || r->header_only))
+            deferred_error = rrl_badmethod09;
     }
 
+    /* Now that the method, uri and protocol are all processed,
+     * we can safely resume any deferred error reporting
+     */
     if (deferred_error != rrl_none) {
         if (deferred_error == rrl_badmethod)
-            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(03445)
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03445)
                           "HTTP Request Line; Invalid method token: '%.*s'",
                           field_name_len(r->method), r->method);
+        else if (deferred_error == rrl_badmethod09)
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 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);
         else if (deferred_error == rrl_missinguri)
-            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(03446)
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03446)
                           "HTTP Request Line; Missing URI");
+        else if (deferred_error == rrl_baduri)
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03454)
+                          "HTTP Request Line; URI incorrectly encoded: '%.*s'",
+                          field_name_len(r->uri), r->uri);
         else if (deferred_error == rrl_badwhitespace)
-            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(03447)
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 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");
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03448)
+                          "HTTP Request Line; Excess whitespace "
+                          "(disallowed by HttpProtocolOptions Strict");
         else if (deferred_error == rrl_trailingtext)
-            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(03449)
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 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)
+        else if (deferred_error == rrl_reject09)
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02401)
+                          "HTTP Request Line; Rejected HTTP/0.9 request");
+        else if (deferred_error == rrl_badprotocol)
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 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;
+        goto rrl_failed;
     }
 
     if (conf->http_methods == AP_HTTP_METHODS_REGISTERED
             && r->method_number == M_INVALID) {
-        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02423)
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 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;
+        /* This can't happen in an HTTP/0.9 request, we verified GET above */
         return 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;
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03450)
+                      "HTTP Request Line; Unable to parse URI: '%.*s'",
+                      field_name_len(r->uri), r->uri);
+        goto rrl_failed;
     }
 
     if (strict) {
-        if (ap_has_cntrl(r->the_request)) {
-            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02420)
+        /* No sense re-testing here for what was evaulated above */
+        if (!stricturi && ap_has_cntrl(r->the_request)) {
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02420)
                           "HTTP Request Line; URI must not contain control"
                           " characters");
             r->status = HTTP_BAD_REQUEST;
-            return 0;
+            goto rrl_failed;
         }
         if (r->parsed_uri.fragment) {
             /* RFC3986 3.5: no fragment */
-            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02421)
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02421)
                           "HTTP Request Line; URI must not contain a fragment");
             r->status = HTTP_BAD_REQUEST;
-            return 0;
+            goto rrl_failed;
         }
         if (r->parsed_uri.user || r->parsed_uri.password) {
-            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02422)
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02422)
                           "HTTP Request Line; URI must not contain a "
                           "username/password");
             r->status = HTTP_BAD_REQUEST;
-            return 0;
+            goto rrl_failed;
         }
     }
 
     return 1;
+
+rrl_failed:
+    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";
+    }
+    return 0;
 }
 
 static int table_do_fn_check_lengths(void *r_, const char *key,
@@ -878,7 +952,6 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb
     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);
-    int strictspaces = (conf->http_whitespace == AP_HTTP_WHITESPACE_STRICT);
 
     /*
      * Read header lines until we get the empty separator line, a read error,
@@ -889,7 +962,7 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb
 
         field = NULL;
         rv = ap_rgetline(&field, r->server->limit_req_fieldsize + 2,
-                         &len, r, 0, bb);
+                         &len, r, strict ? AP_GETLINE_CRLF : 0, bb);
 
         if (rv != APR_SUCCESS) {
             if (APR_STATUS_IS_TIMEUP(rv)) {
@@ -937,7 +1010,7 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb
 
             if (last_field == NULL) {
                 r->status = HTTP_BAD_REQUEST;
-                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(03442)
+                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03442)
                               "Line folding encountered before first"
                               " header line");
                 return;
@@ -945,7 +1018,7 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb
 
             if (field[1] == '\0') {
                 r->status = HTTP_BAD_REQUEST;
-                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(03443)
+                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03443)
                               "Empty folded line encountered");
                 return;
             }
@@ -991,9 +1064,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 (strict || strictspaces) {
-                last_field[last_len] = ' ';
-            }
+            last_field[last_len] = ' ';
             last_len += len;
 
             /* We've appended this obs-fold line to last_len, proceed to
@@ -1024,22 +1095,9 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb
             {
                 /* Not Strict ('Unsafe' mode), using the legacy parser */
 
-                if (strictspaces && strpbrk(last_field, "\n\v\f\r")) {
-                    r->status = HTTP_BAD_REQUEST;
-                    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(03451)
-                                  "Request header presented bad whitespace "
-                                  "(disallowed by StrictWhitespace)");
-                    return;
-                }
-                else {
-                    char *ll = last_field;
-                    while ((ll = strpbrk(ll, "\n\v\f\r")))
-                        *(ll++) = ' ';
-                }
-
                 if (!(value = strchr(last_field, ':'))) { /* Find ':' or */
                     r->status = HTTP_BAD_REQUEST;   /* abort bad request */
-                    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00564)
+                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00564)
                                   "Request header field is missing ':' "
                                   "separator: %.*s", (int)LOG_NAME_MAX_LEN,
                                   last_field);
@@ -1051,11 +1109,11 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb
 
                 *value++ = '\0'; /* NUL-terminate at colon */
 
-                if (strictspaces && strpbrk(last_field, " \t")) {
+                if (strpbrk(last_field, "\t\n\v\f\r ")) {
                     r->status = HTTP_BAD_REQUEST;
-                    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(03452)
-                                  "Request header field name with whitespace "
-                                  "(disallowed by StrictWhitespace)");
+                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03452)
+                                  "Request header field name presented"
+                                  " invalid whitespace");
                     return;
                 }
 
@@ -1063,15 +1121,17 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb
                      ++value;            /* Skip to start of value   */
                 }
 
-                /* Strip LWS after field-name: */
-                while (tmp_field > last_field
-                           && (*tmp_field == ' ' || *tmp_field == '\t')) {
-                    *(tmp_field--) = '\0';
+                if (strpbrk(value, "\n\v\f\r")) {
+                    r->status = HTTP_BAD_REQUEST;
+                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03451)
+                                  "Request header field value presented"
+                                  " bad whitespace");
+                    return;
                 }
 
                 if (tmp_field == last_field) {
                     r->status = HTTP_BAD_REQUEST;
-                    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(03453)
+                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03453)
                                   "Request header field name was empty");
                     return;
                 }
@@ -1082,7 +1142,7 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb
                 value = (char *)ap_scan_http_token(last_field);
                 if ((value == last_field) || *value != ':') {
                     r->status = HTTP_BAD_REQUEST;
-                    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02426)
+                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02426)
                                   "Request header field name is malformed: "
                                   "%.*s", (int)LOG_NAME_MAX_LEN, last_field);
                     return;
@@ -1104,7 +1164,7 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb
                  */
                 if (*tmp_field != '\0') {
                     r->status = HTTP_BAD_REQUEST;
-                    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02427)
+                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02427)
                                   "Request header value is malformed: "
                                   "%.*s", (int)LOG_NAME_MAX_LEN, value);
                     return;
@@ -1225,7 +1285,7 @@ request_rec *ap_read_request(conn_rec *conn)
                               r->server->limit_req_line);
             }
             else if (r->method == NULL) {
-                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00566)
+                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00566)
                               "request failed: malformed request line");
             }
             access_status = r->status;
@@ -1265,7 +1325,7 @@ request_rec *ap_read_request(conn_rec *conn)
 
         ap_get_mime_headers_core(r, tmp_bb);
         if (r->status != HTTP_OK) {
-            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00567)
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00567)
                           "request failed: error reading the headers");
             ap_send_error_response(r, 0);
             ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
@@ -1284,7 +1344,7 @@ request_rec *ap_read_request(conn_rec *conn)
              */
             if (!(strcasecmp(tenc, "chunked") == 0 /* fast path */
                     || ap_find_last_token(r->pool, tenc, "chunked"))) {
-                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02539)
+                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02539)
                               "client sent unknown Transfer-Encoding "
                               "(%s): %s", tenc, r->uri);
                 r->status = HTTP_BAD_REQUEST;
@@ -1305,25 +1365,6 @@ request_rec *ap_read_request(conn_rec *conn)
             apr_table_unset(r->headers_in, "Content-Length");
         }
     }
-    else {
-        if (r->header_only) {
-            /*
-             * Client asked for headers only with HTTP/0.9, which doesn't send
-             * headers! Have to dink things just to make sure the error message
-             * comes through...
-             */
-            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00568)
-                          "client sent invalid HTTP/0.9 request: HEAD %s",
-                          r->uri);
-            r->header_only = 0;
-            r->status = HTTP_BAD_REQUEST;
-            ap_send_error_response(r, 0);
-            ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
-            ap_run_log_transaction(r);
-            apr_brigade_destroy(tmp_bb);
-            goto traceout;
-        }
-    }
 
     apr_brigade_destroy(tmp_bb);
 
@@ -1355,7 +1396,7 @@ request_rec *ap_read_request(conn_rec *conn)
          * a Host: header, and the server MUST respond with 400 if it doesn't.
          */
         access_status = HTTP_BAD_REQUEST;
-        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00569)
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00569)
                       "client sent HTTP/1.1 request without hostname "
                       "(see RFC2616 section 14.23): %s", r->uri);
     }
index d878c32990c14ae2fa77fbcad1bf1e7d26a498b8..e8d4a5a78316e6a5290ce00057a43a9efb983840 100644 (file)
@@ -1633,6 +1633,16 @@ AP_DECLARE(char *) ap_get_http_token(apr_pool_t *p, const char **ptr)
     return tok;
 }
 
+/* Scan a string for valid URI characters per RFC3986, and 
+ * return a pointer to the first non-URI character encountered.
+ */
+AP_DECLARE(const char *) ap_scan_http_uri_safe(const char *ptr)
+{
+    for ( ; TEST_CHAR(*ptr, T_URI_RFC3986); ++ptr) ;
+
+    return ptr;
+}
+
 /* Retrieve a token, spacing over it and returning a pointer to
  * the first non-white byte afterwards.  Note that these tokens
  * are delimited by semis and commas; and can also be delimited