Blame SOURCES/httpd-2.4.6-CVE-2016-8743.patch

21495e
21495e
https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2016-8743
21495e
21495e
diff -uap httpd-2.4.6/docs/manual/mod/core.html.en.cve8743 httpd-2.4.6/docs/manual/mod/core.html.en
21495e
--- httpd-2.4.6/docs/manual/mod/core.html.en.cve8743
21495e
+++ httpd-2.4.6/docs/manual/mod/core.html.en
21495e
@@ -67,6 +67,7 @@
21495e
 
  • ForceType
  • 21495e
     
  • GprofDir
  • 21495e
     
  • HostnameLookups
  • 21495e
    +
  • HttpProtocolOptions
  • 21495e
     
  • <If>
  • 21495e
     
  • <IfDefine>
  • 21495e
     
  • <IfModule>
  • 21495e
    @@ -93,6 +94,7 @@
    21495e
     
  • NameVirtualHost
  • 21495e
     
  • Options
  • 21495e
     
  • Protocol
  • 21495e
    +
  • RegisterHttpMethod
  • 21495e
     
  • RLimitCPU
  • 21495e
     
  • RLimitMEM
  • 21495e
     
  • RLimitNPROC
  • 21495e
    @@ -1918,6 +1920,74 @@
    21495e
     
    21495e
     
    21495e
     
    top
    21495e
    +
    21495e
    +
    21495e
    +Description:Modify restrictions on HTTP Request Messages
    21495e
    +Syntax:HttpProtocolOptions [Strict|Unsafe] [RegisteredMethods|LenientMethods]
    21495e
    + [Allow0.9|Require1.0]
    21495e
    +Default:HttpProtocolOptions Strict LenientMethods Allow0.9
    21495e
    +Context:server config, virtual host
    21495e
    +Status:Core
    21495e
    +Module:core
    21495e
    +Compatibility:2.2.32 or 2.4.24 and later
    21495e
    +
    21495e
    +    

    This directive changes the rules applied to the HTTP Request Line

    21495e
    +    (RFC 7230 §3.1.1) and the HTTP Request Header Fields
    21495e
    +    (RFC 7230 §3.2), which are now applied by default or using
    21495e
    +    the Strict option. Due to legacy modules, applications or
    21495e
    +    custom user-agents which must be deperecated the Unsafe
    21495e
    +    option has been added to revert to the legacy behaviors. These rules
    21495e
    +    are applied prior to request processing, so must be configured at the
    21495e
    +    global or default (first) matching virtual host section, by IP/port
    21495e
    +    interface (and not by name) to be honored.

    21495e
    +
    21495e
    +    

    Prior to the introduction of this directive, the Apache HTTP Server

    21495e
    +    request message parsers were tolerant of a number of forms of input
    21495e
    +    which did not conform to the protocol.
    21495e
    +    RFC 7230 §9.4 Request Splitting and
    21495e
    +    §9.5 Response Smuggling call out only two of the potential
    21495e
    +    risks of accepting non-conformant request messages, while
    21495e
    +    RFC 7230 §3.5 "Message Parsing Robustness" identify the
    21495e
    +    risks of accepting obscure whitespace and request message formatting. 
    21495e
    +    As of the introduction of this directive, all grammer rules of the
    21495e
    +    specification are enforced in the default Strict operating
    21495e
    +    mode, and the strict whitespace suggested by section 3.5 is enforced
    21495e
    +    and cannot be relaxed.

    21495e
    +
    21495e
    +    

    Users are strongly cautioned against toggling the Unsafe

    21495e
    +    mode of operation, particularly on outward-facing, publicly accessible
    21495e
    +    server deployments.  If an interface is required for faulty monitoring
    21495e
    +    or other custom service consumers running on an intranet, users should
    21495e
    +    toggle the Unsafe option only on a specific virtual host configured
    21495e
    +    to service their internal private network.

    21495e
    +
    21495e
    +    

    Reviewing the messages logged to the ErrorLog,

    21495e
    +    configured with LogLevel debug level,
    21495e
    +    can help identify such faulty requests along with their origin.
    21495e
    +    Users should pay particular attention to the 400 responses in the access
    21495e
    +    log for invalid requests which were unexpectedly rejected.

    21495e
    +
    21495e
    +    

    RFC 7231 §4.1 "Request Methods" "Overview" requires that

    21495e
    +    origin servers shall respond with an error when an unsupported method
    21495e
    +    is encountered in the request line. This already happens when the
    21495e
    +    LenientMethods option is used, but administrators may wish
    21495e
    +    to toggle the RegisteredMethods option and register any
    21495e
    +    non-standard methods using the RegisterHttpMethod
    21495e
    +    directive, particularly if the Unsafe option has been toggled.
    21495e
    +    The RegisteredMethods option should not
    21495e
    +    be toggled for forward proxy hosts, as the methods supported by the
    21495e
    +    origin servers are unknown to the proxy server.

    21495e
    +
    21495e
    +    

    RFC 2616 §19.6 "Compatibility With Previous Versions" had

    21495e
    +    encouraged HTTP servers to support legacy HTTP/0.9 requests. RFC 7230
    21495e
    +    superceeds this with "The expectation to support HTTP/0.9 requests has
    21495e
    +    been removed" and offers additional comments in 
    21495e
    +    RFC 7230 Appendix A. The Require1.0 option allows
    21495e
    +    the user to remove support of the default Allow0.9 option's
    21495e
    +    behavior.

    21495e
    +
    21495e
    +
    21495e
    +
    top
    21495e
     
    21495e
     
    21495e
     Description:Contains directives that apply only if a condition is
    21495e
    @@ -3541,6 +3611,23 @@
    21495e
     
    21495e
     
    21495e
     
    top
    21495e
    +
    21495e
    +
    21495e
    +Description:Register non-standard HTTP methods
    21495e
    +Syntax:RegisterHttpMethod method [method [...]]
    21495e
    +Context:server config
    21495e
    +Status:Core
    21495e
    +Module:core
    21495e
    +
    21495e
    +

    HTTP Methods that are not conforming to the relvant RFCs are normally

    21495e
    +rejected by request processing in Apache HTTPD. To avoid this, modules
    21495e
    +can register non-standard HTTP methods they support.
    21495e
    +The RegisterHttpMethod allows to register such
    21495e
    +methods manually. This can be useful for if such methods are forwared
    21495e
    +for external processing, e.g. to a CGI script.

    21495e
    +
    21495e
    +
    21495e
    +
    top
    21495e
     
    21495e
     
    21495e
     Description:Limits the CPU consumption of processes launched
    21495e
    diff -uap httpd-2.4.6/include/http_core.h.cve8743 httpd-2.4.6/include/http_core.h
    21495e
    --- httpd-2.4.6/include/http_core.h.cve8743
    21495e
    +++ httpd-2.4.6/include/http_core.h
    21495e
    @@ -668,6 +668,21 @@
    21495e
     #define AP_MERGE_TRAILERS_DISABLE  2
    21495e
         int merge_trailers;
    21495e
     
    21495e
    +#define AP_HTTP09_UNSET   0
    21495e
    +#define AP_HTTP09_ENABLE  1
    21495e
    +#define AP_HTTP09_DISABLE 2
    21495e
    +    char http09_enable;
    21495e
    +
    21495e
    +#define AP_HTTP_CONFORMANCE_UNSET     0
    21495e
    +#define AP_HTTP_CONFORMANCE_UNSAFE    1
    21495e
    +#define AP_HTTP_CONFORMANCE_STRICT    2
    21495e
    +    char http_conformance;
    21495e
    +
    21495e
    +#define AP_HTTP_METHODS_UNSET         0
    21495e
    +#define AP_HTTP_METHODS_LENIENT       1
    21495e
    +#define AP_HTTP_METHODS_REGISTERED    2
    21495e
    +    char http_methods;
    21495e
    +
    21495e
     } core_server_config;
    21495e
     
    21495e
     /* for AddOutputFiltersByType in core.c */
    21495e
    diff -uap httpd-2.4.6/include/httpd.h.cve8743 httpd-2.4.6/include/httpd.h
    21495e
    --- httpd-2.4.6/include/httpd.h.cve8743
    21495e
    +++ httpd-2.4.6/include/httpd.h
    21495e
    @@ -1584,6 +1584,28 @@
    21495e
      */
    21495e
     AP_DECLARE(int) ap_unescape_url(char *url);
    21495e
     
    21495e
    +/* Scan a string for field content chars, as defined by RFC7230 section 3.2
    21495e
    + * including VCHAR/obs-text, as well as HT and SP
    21495e
    + * @param ptr The string to scan
    21495e
    + * @return A pointer to the first (non-HT) ASCII ctrl character.
    21495e
    + * @note lws and trailing whitespace are scanned, the caller is responsible
    21495e
    + * for trimming leading and trailing whitespace
    21495e
    + */
    21495e
    +AP_DECLARE(const char *) ap_scan_http_field_content(const char *ptr);
    21495e
    +
    21495e
    +/* Scan a string for token characters, as defined by RFC7230 section 3.2.6
    21495e
    + * @param ptr The string to scan
    21495e
    + * @return A pointer to the first non-token character.
    21495e
    + */
    21495e
    +AP_DECLARE(const char *) ap_scan_http_token(const char *ptr);
    21495e
    +
    21495e
    +/* Scan a string for visible ASCII (0x21-0x7E) or obstext (0x80+)
    21495e
    + * and return a pointer to the first SP/CTL/NUL character encountered.
    21495e
    + * @param ptr The string to scan
    21495e
    + * @return A pointer to the first SP/CTL character.
    21495e
    + */
    21495e
    +AP_DECLARE(const char *) ap_scan_vchar_obstext(const char *ptr);
    21495e
    +
    21495e
     /**
    21495e
      * Unescape a URL, but leaving %2f (slashes) escaped
    21495e
      * @param url The url to unescape
    21495e
    diff -uap httpd-2.4.6/include/http_protocol.h.cve8743 httpd-2.4.6/include/http_protocol.h
    21495e
    --- httpd-2.4.6/include/http_protocol.h.cve8743
    21495e
    +++ httpd-2.4.6/include/http_protocol.h
    21495e
    @@ -582,17 +582,22 @@
    21495e
      */
    21495e
     AP_CORE_DECLARE(void) ap_parse_uri(request_rec *r, const char *uri);
    21495e
     
    21495e
    +#define AP_GETLINE_FOLD 1 /* Whether to merge continuation lines */
    21495e
    +#define AP_GETLINE_CRLF 2 /*Whether line ends must be in the form CR LF */
    21495e
    +
    21495e
     /**
    21495e
      * Get the next line of input for the request
    21495e
      * @param s The buffer into which to read the line
    21495e
      * @param n The size of the buffer
    21495e
      * @param r The request
    21495e
    - * @param fold Whether to merge continuation lines
    21495e
    + * @param flags Bit flag of multiple parsing options
    21495e
    + *              AP_GETLINE_FOLD Whether to merge continuation lines
    21495e
    + *              AP_GETLINE_CRLF Whether line ends must be in the form CR LF
    21495e
      * @return The length of the line, if successful
    21495e
      *         n, if the line is too big to fit in the buffer
    21495e
      *         -1 for miscellaneous errors
    21495e
      */
    21495e
    -AP_DECLARE(int) ap_getline(char *s, int n, request_rec *r, int fold);
    21495e
    +AP_DECLARE(int) ap_getline(char *s, int n, request_rec *r, int flags);
    21495e
     
    21495e
     /**
    21495e
      * Get the next line of input for the request
    21495e
    @@ -610,7 +615,9 @@
    21495e
      * @param n The size of the buffer
    21495e
      * @param read The length of the line.
    21495e
      * @param r The request
    21495e
    - * @param fold Whether to merge continuation lines
    21495e
    + * @param flags Bit flag of multiple parsing options
    21495e
    + *              AP_GETLINE_FOLD Whether to merge continuation lines
    21495e
    + *              AP_GETLINE_CRLF Whether line ends must be in the form CR LF
    21495e
      * @param bb Working brigade to use when reading buckets
    21495e
      * @return APR_SUCCESS, if successful
    21495e
      *         APR_ENOSPC, if the line is too big to fit in the buffer
    21495e
    @@ -619,7 +626,7 @@
    21495e
     #if APR_CHARSET_EBCDIC
    21495e
     AP_DECLARE(apr_status_t) ap_rgetline(char **s, apr_size_t n,
    21495e
                                          apr_size_t *read,
    21495e
    -                                     request_rec *r, int fold,
    21495e
    +                                     request_rec *r, int flags,
    21495e
                                          apr_bucket_brigade *bb);
    21495e
     #else /* ASCII box */
    21495e
     #define ap_rgetline(s, n, read, r, fold, bb) \
    21495e
    @@ -629,7 +636,7 @@
    21495e
     /** @see ap_rgetline */
    21495e
     AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
    21495e
                                               apr_size_t *read,
    21495e
    -                                          request_rec *r, int fold,
    21495e
    +                                          request_rec *r, int flags,
    21495e
                                               apr_bucket_brigade *bb);
    21495e
     
    21495e
     /**
    21495e
    diff -uap httpd-2.4.6/modules/http/http_filters.c.cve8743 httpd-2.4.6/modules/http/http_filters.c
    21495e
    --- httpd-2.4.6/modules/http/http_filters.c.cve8743
    21495e
    +++ httpd-2.4.6/modules/http/http_filters.c
    21495e
    @@ -126,14 +126,15 @@
    21495e
     
    21495e
     /**
    21495e
      * Parse a chunk line with optional extension, detect overflow.
    21495e
    - * There are two error cases:
    21495e
    - *  1) If the conversion would require too many bits, APR_EGENERAL is returned.
    21495e
    - *  2) If the conversion used the correct number of bits, but an overflow
    21495e
    + * There are several error cases:
    21495e
    + *  1) If the chunk link is misformatted, APR_EINVAL is returned.
    21495e
    + *  2) If the conversion would require too many bits, APR_EGENERAL is returned.
    21495e
    + *  3) If the conversion used the correct number of bits, but an overflow
    21495e
      *     caused only the sign bit to flip, then APR_ENOSPC is returned.
    21495e
    - * In general, any negative number can be considered an overflow error.
    21495e
    + * A negative chunk length always indicates an overflow error.
    21495e
      */
    21495e
     static apr_status_t parse_chunk_size(http_ctx_t *ctx, const char *buffer,
    21495e
    -                                     apr_size_t len, int linelimit)
    21495e
    +                                     apr_size_t len, int linelimit, int strict)
    21495e
     {
    21495e
         apr_size_t i = 0;
    21495e
     
    21495e
    @@ -146,6 +147,12 @@
    21495e
             if (ctx->state == BODY_CHUNK_END
    21495e
                     || ctx->state == BODY_CHUNK_END_LF) {
    21495e
                 if (c == LF) {
    21495e
    +                if (strict && (ctx->state != BODY_CHUNK_END_LF)) {
    21495e
    +                    /*
    21495e
    +                     * CR missing before LF.
    21495e
    +                     */
    21495e
    +                    return APR_EINVAL;
    21495e
    +                }
    21495e
                     ctx->state = BODY_CHUNK;
    21495e
                 }
    21495e
                 else if (c == CR && ctx->state == BODY_CHUNK_END) {
    21495e
    @@ -153,7 +160,7 @@
    21495e
                 }
    21495e
                 else {
    21495e
                     /*
    21495e
    -                 * LF expected.
    21495e
    +                 * CRLF expected.
    21495e
                      */
    21495e
                     return APR_EINVAL;
    21495e
                 }
    21495e
    @@ -180,6 +187,12 @@
    21495e
             }
    21495e
     
    21495e
             if (c == LF) {
    21495e
    +            if (strict && (ctx->state != BODY_CHUNK_LF)) {
    21495e
    +                /*
    21495e
    +                 * CR missing before LF.
    21495e
    +                 */
    21495e
    +                return APR_EINVAL;
    21495e
    +            }
    21495e
                 if (ctx->remaining) {
    21495e
                     ctx->state = BODY_CHUNK_DATA;
    21495e
                 }
    21495e
    @@ -201,14 +214,17 @@
    21495e
             }
    21495e
             else if (ctx->state == BODY_CHUNK_EXT) {
    21495e
                 /*
    21495e
    -             * Control chars (but tabs) are invalid.
    21495e
    +             * Control chars (excluding tabs) are invalid.
    21495e
    +             * TODO: more precisely limit input
    21495e
                  */
    21495e
                 if (c != '\t' && apr_iscntrl(c)) {
    21495e
                     return APR_EINVAL;
    21495e
                 }
    21495e
             }
    21495e
             else if (c == ' ' || c == '\t') {
    21495e
    -            /* Be lenient up to 10 BWS (term from rfc7230 - 3.2.3).
    21495e
    +            /* Be lenient up to 10 implied *LWS, a legacy of RFC 2616,
    21495e
    +             * and noted as errata to RFC7230;
    21495e
    +             * https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4667
    21495e
                  */
    21495e
                 ctx->state = BODY_CHUNK_CR;
    21495e
                 if (++ctx->chunk_bws > 10) {
    21495e
    @@ -324,7 +340,10 @@
    21495e
                                 ap_input_mode_t mode, apr_read_type_e block,
    21495e
                                 apr_off_t readbytes)
    21495e
     {
    21495e
    -    core_server_config *conf;
    21495e
    +    core_server_config *conf =
    21495e
    +        (core_server_config *) ap_get_module_config(f->r->server->module_config,
    21495e
    +                                                    &core_module);
    21495e
    +    int strict = (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE);
    21495e
         apr_bucket *e;
    21495e
         http_ctx_t *ctx = f->ctx;
    21495e
         apr_status_t rv;
    21495e
    @@ -332,9 +351,6 @@
    21495e
         apr_bucket_brigade *bb;
    21495e
         int again;
    21495e
     
    21495e
    -    conf = (core_server_config *)
    21495e
    -        ap_get_module_config(f->r->server->module_config, &core_module);
    21495e
    -
    21495e
         /* just get out of the way of things we don't want. */
    21495e
         if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE) {
    21495e
             return ap_get_brigade(f->next, b, mode, block, readbytes);
    21495e
    @@ -526,7 +542,7 @@
    21495e
                         if (rv == APR_SUCCESS) {
    21495e
                             parsing = 1;
    21495e
                             rv = parse_chunk_size(ctx, buffer, len,
    21495e
    -                                f->r->server->limit_req_fieldsize);
    21495e
    +                                f->r->server->limit_req_fieldsize, strict);
    21495e
                         }
    21495e
                         if (rv != APR_SUCCESS) {
    21495e
                             ap_log_rerror(APLOG_MARK, APLOG_INFO, rv, f->r, APLOGNO(01590)
    21495e
    @@ -668,14 +684,121 @@
    21495e
         return APR_SUCCESS;
    21495e
     }
    21495e
     
    21495e
    +struct check_header_ctx {
    21495e
    +    request_rec *r;
    21495e
    +    int strict;
    21495e
    +};
    21495e
    +
    21495e
    +/* check a single header, to be used with apr_table_do() */
    21495e
    +static int check_header(struct check_header_ctx *ctx,
    21495e
    +                        const char *name, const char **val)
    21495e
    +{
    21495e
    +    const char *pos, *end;
    21495e
    +    char *dst = NULL;
    21495e
    +
    21495e
    +    if (name[0] == '\0') {
    21495e
    +        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, APLOGNO(02428)
    21495e
    +                      "Empty response header name, aborting request");
    21495e
    +        return 0;
    21495e
    +    }
    21495e
    +
    21495e
    +    if (ctx->strict) {
    21495e
    +        end = ap_scan_http_token(name);
    21495e
    +    }
    21495e
    +    else {
    21495e
    +        end = ap_scan_vchar_obstext(name);
    21495e
    +    }
    21495e
    +    if (*end) {
    21495e
    +        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, APLOGNO(02429)
    21495e
    +                      "Response header name '%s' contains invalid "
    21495e
    +                      "characters, aborting request",
    21495e
    +                      name);
    21495e
    +        return 0;
    21495e
    +    }
    21495e
    +
    21495e
    +    for (pos = *val; *pos; pos = end) {
    21495e
    +        end = ap_scan_http_field_content(pos);
    21495e
    +        if (*end) {
    21495e
    +            if (end[0] != CR || end[1] != LF || (end[2] != ' ' &&
    21495e
    +                                                 end[2] != '\t')) {
    21495e
    +                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, APLOGNO(02430)
    21495e
    +                              "Response header '%s' value of '%s' contains "
    21495e
    +                              "invalid characters, aborting request",
    21495e
    +                              name, pos);
    21495e
    +                return 0;
    21495e
    +            }
    21495e
    +            if (!dst) {
    21495e
    +                *val = dst = apr_palloc(ctx->r->pool, strlen(*val) + 1);
    21495e
    +            }
    21495e
    +        }
    21495e
    +        if (dst) {
    21495e
    +            memcpy(dst, pos, end - pos);
    21495e
    +            dst += end - pos;
    21495e
    +            if (*end) {
    21495e
    +                /* skip folding and replace with a single space */
    21495e
    +                end += 3 + strspn(end + 3, "\t ");
    21495e
    +                *dst++ = ' ';
    21495e
    +            }
    21495e
    +        }
    21495e
    +    }
    21495e
    +    if (dst) {
    21495e
    +        *dst = '\0';
    21495e
    +    }
    21495e
    +    return 1;
    21495e
    +}
    21495e
    +
    21495e
    +static int check_headers_table(apr_table_t *t, struct check_header_ctx *ctx)
    21495e
    +{
    21495e
    +    const apr_array_header_t *headers = apr_table_elts(t);
    21495e
    +    apr_table_entry_t *header;
    21495e
    +    int i;
    21495e
    +
    21495e
    +    for (i = 0; i < headers->nelts; ++i) {
    21495e
    +        header = &APR_ARRAY_IDX(headers, i, apr_table_entry_t);
    21495e
    +        if (!header->key) {
    21495e
    +            continue;
    21495e
    +        }
    21495e
    +        if (!check_header(ctx, header->key, (const char **)&header->val)) {
    21495e
    +            return 0;
    21495e
    +        }
    21495e
    +    }
    21495e
    +    return 1;
    21495e
    +}
    21495e
    +
    21495e
    +/**
    21495e
    + * Check headers for HTTP conformance
    21495e
    + * @return 1 if ok, 0 if bad
    21495e
    + */
    21495e
    +static APR_INLINE int check_headers(request_rec *r)
    21495e
    +{
    21495e
    +    struct check_header_ctx ctx;
    21495e
    +    core_server_config *conf =
    21495e
    +            ap_get_core_module_config(r->server->module_config);
    21495e
    +
    21495e
    +    ctx.r = r;
    21495e
    +    ctx.strict = (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE);
    21495e
    +    return check_headers_table(r->headers_out, &ctx) &&
    21495e
    +           check_headers_table(r->err_headers_out, &ctx;;
    21495e
    +}
    21495e
    +
    21495e
    +static int check_headers_recursion(request_rec *r)
    21495e
    +{
    21495e
    +    void *check = NULL;
    21495e
    +    apr_pool_userdata_get(&check, "check_headers_recursion", r->pool);
    21495e
    +    if (check) {
    21495e
    +        return 1;
    21495e
    +    }
    21495e
    +    apr_pool_userdata_setn("true", "check_headers_recursion", NULL, r->pool);
    21495e
    +    return 0;
    21495e
    +}
    21495e
    +
    21495e
     typedef struct header_struct {
    21495e
         apr_pool_t *pool;
    21495e
         apr_bucket_brigade *bb;
    21495e
     } header_struct;
    21495e
     
    21495e
     /* Send a single HTTP header field to the client.  Note that this function
    21495e
    - * is used in calls to table_do(), so their interfaces are co-dependent.
    21495e
    - * In other words, don't change this one without checking table_do in alloc.c.
    21495e
    + * is used in calls to apr_table_do(), so don't change its interface.
    21495e
      * It returns true unless there was a write error of some kind.
    21495e
      */
    21495e
     static int form_header_field(header_struct *h,
    21495e
    @@ -1160,6 +1283,7 @@
    21495e
     
    21495e
     typedef struct header_filter_ctx {
    21495e
         int headers_sent;
    21495e
    +    int headers_error;
    21495e
     } header_filter_ctx;
    21495e
     
    21495e
     AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f,
    21495e
    @@ -1175,19 +1299,23 @@
    21495e
         header_filter_ctx *ctx = f->ctx;
    21495e
         const char *ctype;
    21495e
         ap_bucket_error *eb = NULL;
    21495e
    +    apr_bucket *eos = NULL;
    21495e
     
    21495e
         AP_DEBUG_ASSERT(!r->main);
    21495e
     
    21495e
    -    if (r->header_only) {
    21495e
    -        if (!ctx) {
    21495e
    -            ctx = f->ctx = apr_pcalloc(r->pool, sizeof(header_filter_ctx));
    21495e
    -        }
    21495e
    -        else if (ctx->headers_sent) {
    21495e
    +    if (!ctx) {
    21495e
    +        ctx = f->ctx = apr_pcalloc(r->pool, sizeof(header_filter_ctx));
    21495e
    +    }
    21495e
    +    if (ctx->headers_sent) {
    21495e
    +        /* Eat body if response must not have one. */
    21495e
    +        if (r->header_only || r->status == HTTP_NO_CONTENT) {
    21495e
                 apr_brigade_cleanup(b);
    21495e
    -            return OK;
    21495e
    +            return APR_SUCCESS;
    21495e
             }
    21495e
         }
    21495e
    -
    21495e
    +    else if (!ctx->headers_error && !check_headers(r)) {
    21495e
    +        ctx->headers_error = 1;
    21495e
    +    }
    21495e
         for (e = APR_BRIGADE_FIRST(b);
    21495e
              e != APR_BRIGADE_SENTINEL(b);
    21495e
              e = APR_BUCKET_NEXT(e))
    21495e
    @@ -1204,10 +1332,44 @@
    21495e
                 ap_remove_output_filter(f);
    21495e
                 return ap_pass_brigade(f->next, b);
    21495e
             }
    21495e
    +        if (ctx->headers_error && APR_BUCKET_IS_EOS(e)) {
    21495e
    +            eos = e;
    21495e
    +        }
    21495e
         }
    21495e
    -    if (eb) {
    21495e
    -        int status;
    21495e
    +    if (ctx->headers_error) {
    21495e
    +        if (!eos) {
    21495e
    +            /* Eat body until EOS */
    21495e
    +            apr_brigade_cleanup(b);
    21495e
    +            return APR_SUCCESS;
    21495e
    +        }
    21495e
     
    21495e
    +        /* We may come back here from ap_die() below,
    21495e
    +         * so clear anything from this response.
    21495e
    +         */
    21495e
    +        ctx->headers_error = 0;
    21495e
    +        apr_table_clear(r->headers_out);
    21495e
    +        apr_table_clear(r->err_headers_out);
    21495e
    +
    21495e
    +        /* Don't recall ap_die() if we come back here (from its own internal
    21495e
    +         * redirect or error response), otherwise we can end up in infinite
    21495e
    +         * recursion; better fall through with 500, minimal headers and an
    21495e
    +         * empty body (EOS only).
    21495e
    +         */
    21495e
    +        if (!check_headers_recursion(r)) {
    21495e
    +            apr_brigade_cleanup(b);
    21495e
    +            ap_die(HTTP_INTERNAL_SERVER_ERROR, r);
    21495e
    +            return AP_FILTER_ERROR;
    21495e
    +        }
    21495e
    +        APR_BUCKET_REMOVE(eos);
    21495e
    +        apr_brigade_cleanup(b);
    21495e
    +        APR_BRIGADE_INSERT_TAIL(b, eos);
    21495e
    +        r->status = HTTP_INTERNAL_SERVER_ERROR;
    21495e
    +        r->content_type = r->content_encoding = NULL;
    21495e
    +        r->content_languages = NULL;
    21495e
    +        ap_set_content_length(r, 0);
    21495e
    +    }
    21495e
    +    else if (eb) {
    21495e
    +        int status;
    21495e
             status = eb->status;
    21495e
             apr_brigade_cleanup(b);
    21495e
             ap_die(status, r);
    21495e
    @@ -1264,6 +1426,10 @@
    21495e
             apr_table_unset(r->headers_out, "Content-Length");
    21495e
         }
    21495e
     
    21495e
    +    if (r->status == HTTP_NO_CONTENT) {
    21495e
    +        apr_table_unset(r->headers_out, "Content-Length");
    21495e
    +    }
    21495e
    +
    21495e
         ctype = ap_make_content_type(r, r->content_type);
    21495e
         if (ctype) {
    21495e
             apr_table_setn(r->headers_out, "Content-Type", ctype);
    21495e
    @@ -1352,11 +1518,11 @@
    21495e
         terminate_header(b2);
    21495e
     
    21495e
         ap_pass_brigade(f->next, b2);
    21495e
    +    ctx->headers_sent = 1;
    21495e
     
    21495e
    -    if (r->header_only) {
    21495e
    +    if (r->header_only || r->status == HTTP_NO_CONTENT) {
    21495e
             apr_brigade_cleanup(b);
    21495e
    -        ctx->headers_sent = 1;
    21495e
    -        return OK;
    21495e
    +        return APR_SUCCESS;
    21495e
         }
    21495e
     
    21495e
         r->sent_bodyct = 1;         /* Whatever follows is real body stuff... */
    21495e
    diff -uap httpd-2.4.6/server/core.c.cve8743 httpd-2.4.6/server/core.c
    21495e
    --- httpd-2.4.6/server/core.c.cve8743
    21495e
    +++ httpd-2.4.6/server/core.c
    21495e
    @@ -506,6 +506,15 @@
    21495e
         if (virt->trace_enable != AP_TRACE_UNSET)
    21495e
             conf->trace_enable = virt->trace_enable;
    21495e
     
    21495e
    +    if (virt->http09_enable != AP_HTTP09_UNSET)
    21495e
    +        conf->http09_enable = virt->http09_enable;
    21495e
    +
    21495e
    +    if (virt->http_conformance != AP_HTTP_CONFORMANCE_UNSET)
    21495e
    +        conf->http_conformance = virt->http_conformance;
    21495e
    +
    21495e
    +    if (virt->http_methods != AP_HTTP_METHODS_UNSET)
    21495e
    +        conf->http_methods = virt->http_methods;
    21495e
    +
    21495e
         /* no action for virt->accf_map, not allowed per-vhost */
    21495e
     
    21495e
         if (virt->protocol)
    21495e
    @@ -3632,6 +3641,57 @@
    21495e
         return NULL;
    21495e
     }
    21495e
     
    21495e
    +static const char *set_http_protocol_options(cmd_parms *cmd, void *dummy,
    21495e
    +                                             const char *arg)
    21495e
    +{
    21495e
    +    core_server_config *conf =
    21495e
    +        ap_get_core_module_config(cmd->server->module_config);
    21495e
    +
    21495e
    +    if (strcasecmp(arg, "allow0.9") == 0)
    21495e
    +        conf->http09_enable |= AP_HTTP09_ENABLE;
    21495e
    +    else if (strcasecmp(arg, "require1.0") == 0)
    21495e
    +        conf->http09_enable |= AP_HTTP09_DISABLE;
    21495e
    +    else if (strcasecmp(arg, "strict") == 0)
    21495e
    +        conf->http_conformance |= AP_HTTP_CONFORMANCE_STRICT;
    21495e
    +    else if (strcasecmp(arg, "unsafe") == 0)
    21495e
    +        conf->http_conformance |= AP_HTTP_CONFORMANCE_UNSAFE;
    21495e
    +    else if (strcasecmp(arg, "registeredmethods") == 0)
    21495e
    +        conf->http_methods |= AP_HTTP_METHODS_REGISTERED;
    21495e
    +    else if (strcasecmp(arg, "lenientmethods") == 0)
    21495e
    +        conf->http_methods |= AP_HTTP_METHODS_LENIENT;
    21495e
    +    else
    21495e
    +        return "HttpProtocolOptions accepts "
    21495e
    +               "'Unsafe' or 'Strict' (default), "
    21495e
    +               "'RegisteredMethods' or 'LenientMethods' (default), and "
    21495e
    +               "'Require1.0' or 'Allow0.9' (default)";
    21495e
    +
    21495e
    +    if ((conf->http09_enable & AP_HTTP09_ENABLE)
    21495e
    +            && (conf->http09_enable & AP_HTTP09_DISABLE))
    21495e
    +        return "HttpProtocolOptions 'Allow0.9' and 'Require1.0'"
    21495e
    +               " are mutually exclusive";
    21495e
    +
    21495e
    +    if ((conf->http_conformance & AP_HTTP_CONFORMANCE_STRICT)
    21495e
    +            && (conf->http_conformance & AP_HTTP_CONFORMANCE_UNSAFE))
    21495e
    +        return "HttpProtocolOptions 'Strict' and 'Unsafe'"
    21495e
    +               " are mutually exclusive";
    21495e
    +
    21495e
    +    if ((conf->http_methods & AP_HTTP_METHODS_REGISTERED)
    21495e
    +            && (conf->http_methods & AP_HTTP_METHODS_LENIENT))
    21495e
    +        return "HttpProtocolOptions 'RegisteredMethods' and 'LenientMethods'"
    21495e
    +               " are mutually exclusive";
    21495e
    +
    21495e
    +    return NULL;
    21495e
    +}
    21495e
    +
    21495e
    +static const char *set_http_method(cmd_parms *cmd, void *conf, const char *arg)
    21495e
    +{
    21495e
    +    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
    21495e
    +    if (err != NULL)
    21495e
    +        return err;
    21495e
    +    ap_method_register(cmd->pool, arg);
    21495e
    +    return NULL;
    21495e
    +}
    21495e
    +
    21495e
     static apr_hash_t *errorlog_hash;
    21495e
     
    21495e
     static int log_constant_item(const ap_errorlog_info *info, const char *arg,
    21495e
    @@ -4143,6 +4203,13 @@
    21495e
                   "'on' (default), 'off' or 'extended' to trace request body content"),
    21495e
     AP_INIT_FLAG("MergeTrailers", set_merge_trailers, NULL, RSRC_CONF,
    21495e
                   "merge request trailers into request headers or not"),
    21495e
    +AP_INIT_ITERATE("HttpProtocolOptions", set_http_protocol_options, NULL, RSRC_CONF,
    21495e
    +                "'Allow0.9' or 'Require1.0' (default); "
    21495e
    +                "'RegisteredMethods' or 'LenientMethods' (default); "
    21495e
    +                "'Unsafe' or 'Strict' (default). Sets HTTP acceptance rules")
    21495e
    +,
    21495e
    +AP_INIT_ITERATE("RegisterHttpMethod", set_http_method, NULL, RSRC_CONF,
    21495e
    +                "Registers non-standard HTTP methods"),
    21495e
     { NULL }
    21495e
     };
    21495e
     
    21495e
    diff -uap httpd-2.4.6/server/gen_test_char.c.cve8743 httpd-2.4.6/server/gen_test_char.c
    21495e
    --- httpd-2.4.6/server/gen_test_char.c.cve8743
    21495e
    +++ httpd-2.4.6/server/gen_test_char.c
    21495e
    @@ -16,11 +16,11 @@
    21495e
     
    21495e
     #ifdef CROSS_COMPILE
    21495e
     
    21495e
    +#include <ctype.h>
    21495e
     #define apr_isalnum(c) (isalnum(((unsigned char)(c))))
    21495e
     #define apr_isalpha(c) (isalpha(((unsigned char)(c))))
    21495e
     #define apr_iscntrl(c) (iscntrl(((unsigned char)(c))))
    21495e
     #define apr_isprint(c) (isprint(((unsigned char)(c))))
    21495e
    -#include <ctype.h>
    21495e
     #define APR_HAVE_STDIO_H 1
    21495e
     #define APR_HAVE_STRING_H 1
    21495e
     
    21495e
    @@ -52,11 +52,13 @@
    21495e
     #define T_ESCAPE_LOGITEM      (0x10)
    21495e
     #define T_ESCAPE_FORENSIC     (0x20)
    21495e
     #define T_ESCAPE_URLENCODED   (0x40)
    21495e
    +#define T_HTTP_CTRLS          (0x80)
    21495e
    +#define T_VCHAR_OBSTEXT      (0x100)
    21495e
     
    21495e
     int main(int argc, char *argv[])
    21495e
     {
    21495e
         unsigned c;
    21495e
    -    unsigned char flags;
    21495e
    +    unsigned short flags;
    21495e
     
    21495e
         printf("/* this file is automatically generated by gen_test_char, "
    21495e
                "do not edit */\n"
    21495e
    @@ -67,19 +69,23 @@
    21495e
                "#define T_ESCAPE_LOGITEM       (%u)\n"
    21495e
                "#define T_ESCAPE_FORENSIC      (%u)\n"
    21495e
                "#define T_ESCAPE_URLENCODED    (%u)\n"
    21495e
    +           "#define T_HTTP_CTRLS           (%u)\n"
    21495e
    +           "#define T_VCHAR_OBSTEXT        (%u)\n"
    21495e
                "\n"
    21495e
    -           "static const unsigned char test_char_table[256] = {",
    21495e
    +           "static const unsigned short test_char_table[256] = {",
    21495e
                T_ESCAPE_SHELL_CMD,
    21495e
                T_ESCAPE_PATH_SEGMENT,
    21495e
                T_OS_ESCAPE_PATH,
    21495e
                T_HTTP_TOKEN_STOP,
    21495e
                T_ESCAPE_LOGITEM,
    21495e
                T_ESCAPE_FORENSIC,
    21495e
    -           T_ESCAPE_URLENCODED);
    21495e
    +           T_ESCAPE_URLENCODED,
    21495e
    +           T_HTTP_CTRLS,
    21495e
    +           T_VCHAR_OBSTEXT);
    21495e
     
    21495e
         for (c = 0; c < 256; ++c) {
    21495e
             flags = 0;
    21495e
    -        if (c % 20 == 0)
    21495e
    +        if (c % 8 == 0)
    21495e
                 printf("\n    ");
    21495e
     
    21495e
             /* escape_shell_cmd */
    21495e
    @@ -107,7 +113,7 @@
    21495e
                 flags |= T_ESCAPE_PATH_SEGMENT;
    21495e
             }
    21495e
     
    21495e
    -        if (!apr_isalnum(c) && !strchr("$-_.+!*'(),:@&=/~", c)) {
    21495e
    +        if (!apr_isalnum(c) && !strchr("$-_.+!*'(),:;@&=/~", c)) {
    21495e
                 flags |= T_OS_ESCAPE_PATH;
    21495e
             }
    21495e
     
    21495e
    @@ -115,11 +121,32 @@
    21495e
                 flags |= T_ESCAPE_URLENCODED;
    21495e
             }
    21495e
     
    21495e
    -        /* these are the "tspecials" (RFC2068) or "separators" (RFC2616) */
    21495e
    -        if (c && (apr_iscntrl(c) || strchr(" \t()<>@,;:\\\"/[]?={}", c))) {
    21495e
    +        /* Stop for any non-'token' character, including ctrls, obs-text,
    21495e
    +         * and "tspecials" (RFC2068) a.k.a. "separators" (RFC2616), which
    21495e
    +         * is easer to express as characters remaining in the ASCII token set
    21495e
    +         */
    21495e
    +        if (!c || !(apr_isalnum(c) || strchr("!#$%&'*+-.^_`|~", c))) {
    21495e
                 flags |= T_HTTP_TOKEN_STOP;
    21495e
             }
    21495e
     
    21495e
    +        /* Catch CTRLs other than VCHAR, HT and SP, and obs-text (RFC7230 3.2)
    21495e
    +         * This includes only the C0 plane, not C1 (which is obs-text itself.)
    21495e
    +         * XXX: We should verify that all ASCII C0 ctrls/DEL corresponding to
    21495e
    +         * the current EBCDIC translation are captured, and ASCII C1 ctrls
    21495e
    +         * corresponding are all permitted (as they fall under obs-text rule)
    21495e
    +         */
    21495e
    +        if (!c || (apr_iscntrl(c) && c != '\t')) {
    21495e
    +            flags |= T_HTTP_CTRLS;
    21495e
    +        }
    21495e
    +
    21495e
    +        /* From RFC3986, the specific sets of gen-delims, sub-delims (2.2),
    21495e
    +         * and unreserved (2.3) that are possible somewhere within a URI.
    21495e
    +         * Spec requires all others to be %XX encoded, including obs-text.
    21495e
    +         */
    21495e
    +        if (c && !apr_iscntrl(c) && c != ' ') {
    21495e
    +            flags |= T_VCHAR_OBSTEXT;
    21495e
    +        }
    21495e
    +
    21495e
             /* For logging, escape all control characters,
    21495e
              * double quotes (because they delimit the request in the log file)
    21495e
              * backslashes (because we use backslash for escaping)
    21495e
    @@ -137,7 +164,7 @@
    21495e
                 flags |= T_ESCAPE_FORENSIC;
    21495e
             }
    21495e
     
    21495e
    -        printf("%u%c", flags, (c < 255) ? ',' : ' ');
    21495e
    +        printf("0x%03x%c", flags, (c < 255) ? ',' : ' ');
    21495e
         }
    21495e
     
    21495e
         printf("\n};\n");
    21495e
    diff -uap httpd-2.4.6/server/protocol.c.cve8743 httpd-2.4.6/server/protocol.c
    21495e
    --- httpd-2.4.6/server/protocol.c.cve8743
    21495e
    +++ httpd-2.4.6/server/protocol.c
    21495e
    @@ -189,6 +189,10 @@
    21495e
      * caused by MIME folding (or broken clients) if fold != 0, and place it
    21495e
      * in the buffer s, of size n bytes, without the ending newline.
    21495e
      *
    21495e
    + * Pulls from r->proto_input_filters instead of r->input_filters for
    21495e
    + * stricter protocol adherence and better input filter behavior during
    21495e
    + * chunked trailer processing (for http).
    21495e
    + *
    21495e
      * If s is NULL, ap_rgetline_core will allocate necessary memory from r->pool.
    21495e
      *
    21495e
      * Returns APR_SUCCESS if there are no problems and sets *read to be
    21495e
    @@ -197,7 +201,7 @@
    21495e
      * APR_ENOSPC is returned if there is not enough buffer space.
    21495e
      * Other errors may be returned on other errors.
    21495e
      *
    21495e
    - * The LF is *not* returned in the buffer.  Therefore, a *read of 0
    21495e
    + * The [CR]LF are *not* returned in the buffer.  Therefore, a *read of 0
    21495e
      * indicates that an empty line was read.
    21495e
      *
    21495e
      * Notes: Because the buffer uses 1 char for NUL, the most we can return is
    21495e
    @@ -208,13 +212,15 @@
    21495e
      */
    21495e
     AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
    21495e
                                               apr_size_t *read, request_rec *r,
    21495e
    -                                          int fold, apr_bucket_brigade *bb)
    21495e
    +                                          int flags, apr_bucket_brigade *bb)
    21495e
     {
    21495e
         apr_status_t rv;
    21495e
         apr_bucket *e;
    21495e
         apr_size_t bytes_handled = 0, current_alloc = 0;
    21495e
         char *pos, *last_char = *s;
    21495e
         int do_alloc = (*s == NULL), saw_eos = 0;
    21495e
    +    int fold = flags & AP_GETLINE_FOLD;
    21495e
    +    int crlf = flags & AP_GETLINE_CRLF;
    21495e
     
    21495e
         /*
    21495e
          * Initialize last_char as otherwise a random value will be compared
    21495e
    @@ -226,13 +232,15 @@
    21495e
     
    21495e
         for (;;) {
    21495e
             apr_brigade_cleanup(bb);
    21495e
    -        rv = ap_get_brigade(r->input_filters, bb, AP_MODE_GETLINE,
    21495e
    +        rv = ap_get_brigade(r->proto_input_filters, bb, AP_MODE_GETLINE,
    21495e
                                 APR_BLOCK_READ, 0);
    21495e
             if (rv != APR_SUCCESS) {
    21495e
                 return rv;
    21495e
             }
    21495e
     
    21495e
    -        /* Something horribly wrong happened.  Someone didn't block! */
    21495e
    +        /* Something horribly wrong happened.  Someone didn't block! 
    21495e
    +         * (this also happens at the end of each keepalive connection)
    21495e
    +         */
    21495e
             if (APR_BRIGADE_EMPTY(bb)) {
    21495e
                 return APR_EGENERAL;
    21495e
             }
    21495e
    @@ -318,6 +326,13 @@
    21495e
             }
    21495e
         }
    21495e
     
    21495e
    +    if (crlf && (last_char <= *s || last_char[-1] != APR_ASCII_CR)) {
    21495e
    +        *last_char = '\0';
    21495e
    +        bytes_handled = last_char - *s;
    21495e
    +        *read = bytes_handled;
    21495e
    +        return APR_EINVAL;
    21495e
    +    }
    21495e
    +
    21495e
         /* Now NUL-terminate the string at the end of the line;
    21495e
          * if the last-but-one character is a CR, terminate there */
    21495e
         if (last_char > *s && last_char[-1] == APR_ASCII_CR) {
    21495e
    @@ -340,7 +355,7 @@
    21495e
                 apr_brigade_cleanup(bb);
    21495e
     
    21495e
                 /* We only care about the first byte. */
    21495e
    -            rv = ap_get_brigade(r->input_filters, bb, AP_MODE_SPECULATIVE,
    21495e
    +            rv = ap_get_brigade(r->proto_input_filters, bb, AP_MODE_SPECULATIVE,
    21495e
                                     APR_BLOCK_READ, 1);
    21495e
                 if (rv != APR_SUCCESS) {
    21495e
                     return rv;
    21495e
    @@ -391,7 +406,8 @@
    21495e
                          */
    21495e
                         if (do_alloc) {
    21495e
                             tmp = NULL;
    21495e
    -                    } else {
    21495e
    +                    }
    21495e
    +                    else {
    21495e
                             /* We're null terminated. */
    21495e
                             tmp = last_char;
    21495e
                         }
    21495e
    @@ -461,7 +477,7 @@
    21495e
     }
    21495e
     #endif
    21495e
     
    21495e
    -AP_DECLARE(int) ap_getline(char *s, int n, request_rec *r, int fold)
    21495e
    +AP_DECLARE(int) ap_getline(char *s, int n, request_rec *r, int flags)
    21495e
     {
    21495e
         char *tmp_s = s;
    21495e
         apr_status_t rv;
    21495e
    @@ -469,7 +485,7 @@
    21495e
         apr_bucket_brigade *tmp_bb;
    21495e
     
    21495e
         tmp_bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
    21495e
    -    rv = ap_rgetline(&tmp_s, n, &len, r, fold, tmp_bb);
    21495e
    +    rv = ap_rgetline(&tmp_s, n, &len, r, flags, tmp_bb);
    21495e
         apr_brigade_destroy(tmp_bb);
    21495e
     
    21495e
         /* Map the out-of-space condition to the old API. */
    21495e
    @@ -549,16 +565,29 @@
    21495e
         }
    21495e
     }
    21495e
     
    21495e
    -static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
    21495e
    +/* get the length of the field name for logging, but no more than 80 bytes */
    21495e
    +#define LOG_NAME_MAX_LEN 80
    21495e
    +static int field_name_len(const char *field)
    21495e
     {
    21495e
    -    const char *ll;
    21495e
    -    const char *uri;
    21495e
    -    const char *pro;
    21495e
    +    const char *end = ap_strchr_c(field, ':');
    21495e
    +    if (end == NULL || end - field > LOG_NAME_MAX_LEN)
    21495e
    +        return LOG_NAME_MAX_LEN;
    21495e
    +    return end - field;
    21495e
    +}
    21495e
     
    21495e
    -    int major = 1, minor = 0;   /* Assume HTTP/1.0 if non-"HTTP" protocol */
    21495e
    -    char http[5];
    21495e
    +static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
    21495e
    +{
    21495e
    +    enum {
    21495e
    +        rrl_none, rrl_badmethod, rrl_badwhitespace, rrl_excesswhitespace,
    21495e
    +        rrl_missinguri, rrl_baduri, rrl_badprotocol, rrl_trailingtext,
    21495e
    +        rrl_badmethod09, rrl_reject09
    21495e
    +    } deferred_error = rrl_none;
    21495e
    +    char *ll;
    21495e
    +    char *uri;
    21495e
         apr_size_t len;
    21495e
         int num_blank_lines = 0;
    21495e
    +    core_server_config *conf = ap_get_core_module_config(r->server->module_config);
    21495e
    +    int strict = (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE);
    21495e
         int max_blank_lines = r->server->limit_req_fields;
    21495e
     
    21495e
         if (max_blank_lines <= 0) {
    21495e
    @@ -588,7 +617,7 @@
    21495e
              */
    21495e
             r->the_request = NULL;
    21495e
             rv = ap_rgetline(&(r->the_request), (apr_size_t)(r->server->limit_req_line + 2),
    21495e
    -                         &len, r, 0, bb);
    21495e
    +                         &len, r, strict ? AP_GETLINE_CRLF : 0, bb);
    21495e
     
    21495e
             if (rv != APR_SUCCESS) {
    21495e
                 r->request_time = apr_time_now();
    21495e
    @@ -599,8 +628,6 @@
    21495e
                  */
    21495e
                 if (APR_STATUS_IS_ENOSPC(rv)) {
    21495e
                     r->status    = HTTP_REQUEST_URI_TOO_LARGE;
    21495e
    -                r->proto_num = HTTP_VERSION(1,0);
    21495e
    -                r->protocol  = apr_pstrdup(r->pool, "HTTP/1.0");
    21495e
                 }
    21495e
                 else if (APR_STATUS_IS_TIMEUP(rv)) {
    21495e
                     r->status = HTTP_REQUEST_TIME_OUT;
    21495e
    @@ -608,6 +635,8 @@
    21495e
                 else if (APR_STATUS_IS_EINVAL(rv)) {
    21495e
                     r->status = HTTP_BAD_REQUEST;
    21495e
                 }
    21495e
    +            r->proto_num = HTTP_VERSION(1,0);
    21495e
    +            r->protocol  = apr_pstrdup(r->pool, "HTTP/1.0");
    21495e
                 return 0;
    21495e
             }
    21495e
         } while ((len <= 0) && (++num_blank_lines < max_blank_lines));
    21495e
    @@ -619,46 +648,263 @@
    21495e
         }
    21495e
     
    21495e
         r->request_time = apr_time_now();
    21495e
    -    ll = r->the_request;
    21495e
    -    r->method = ap_getword_white(r->pool, &ll);
    21495e
    +    r->method = r->the_request;
    21495e
    +
    21495e
    +    /* If there is whitespace before a method, skip it and mark in error */
    21495e
    +    if (apr_isspace(*r->method)) {
    21495e
    +        deferred_error = rrl_badwhitespace;
    21495e
    +        for ( ; apr_isspace(*r->method); ++r->method)
    21495e
    +            ;
    21495e
    +    }
    21495e
     
    21495e
    -    uri = ap_getword_white(r->pool, &ll);
    21495e
    +    /* Scan the method up to the next whitespace, ensure it contains only
    21495e
    +     * valid http-token characters, otherwise mark in error
    21495e
    +     */
    21495e
    +    if (strict) {
    21495e
    +        ll = (char*) ap_scan_http_token(r->method);
    21495e
    +    }
    21495e
    +    else {
    21495e
    +        ll = (char*) ap_scan_vchar_obstext(r->method);
    21495e
    +    }
    21495e
     
    21495e
    -    /* Provide quick information about the request method as soon as known */
    21495e
    +    if (((ll == r->method) || (*ll && !apr_isspace(*ll)))
    21495e
    +            && deferred_error == rrl_none) {
    21495e
    +        deferred_error = rrl_badmethod;
    21495e
    +        ll = strpbrk(ll, "\t\n\v\f\r ");
    21495e
    +    }
    21495e
     
    21495e
    -    r->method_number = ap_method_number_of(r->method);
    21495e
    -    if (r->method_number == M_GET && r->method[0] == 'H') {
    21495e
    -        r->header_only = 1;
    21495e
    +    /* Verify method terminated with a single SP, or mark as specific error */
    21495e
    +    if (!ll) {
    21495e
    +        if (deferred_error == rrl_none)
    21495e
    +            deferred_error = rrl_missinguri;
    21495e
    +        r->protocol = uri = "";
    21495e
    +        len = 0;
    21495e
    +        goto rrl_done;
    21495e
    +    }
    21495e
    +    else if (strict && ll[0] && apr_isspace(ll[1])
    21495e
    +             && deferred_error == rrl_none) {
    21495e
    +        deferred_error = rrl_excesswhitespace;
    21495e
         }
    21495e
     
    21495e
    -    ap_parse_uri(r, uri);
    21495e
    +    /* Advance uri pointer over leading whitespace, NUL terminate the method
    21495e
    +     * If non-SP whitespace is encountered, mark as specific error
    21495e
    +     */
    21495e
    +    for (uri = ll; apr_isspace(*uri); ++uri)
    21495e
    +        if (*uri != ' ' && deferred_error == rrl_none)
    21495e
    +            deferred_error = rrl_badwhitespace;
    21495e
    +    *ll = '\0';
    21495e
    +
    21495e
    +    if (!*uri && deferred_error == rrl_none)
    21495e
    +        deferred_error = rrl_missinguri;
    21495e
    +
    21495e
    +    /* Scan the URI up to the next whitespace, ensure it contains no raw
    21495e
    +     * control characters, otherwise mark in error
    21495e
    +     */
    21495e
    +    ll = (char*) ap_scan_vchar_obstext(uri);
    21495e
    +    if (ll == uri || (*ll && !apr_isspace(*ll))) {
    21495e
    +        deferred_error = rrl_baduri;
    21495e
    +        ll = strpbrk(ll, "\t\n\v\f\r ");
    21495e
    +    }
    21495e
     
    21495e
    -    if (ll[0]) {
    21495e
    +    /* Verify URI terminated with a single SP, or mark as specific error */
    21495e
    +    if (!ll) {
    21495e
    +        r->protocol = "";
    21495e
    +        len = 0;
    21495e
    +        goto rrl_done;
    21495e
    +    }
    21495e
    +    else if (strict && ll[0] && apr_isspace(ll[1])
    21495e
    +             && deferred_error == rrl_none) {
    21495e
    +        deferred_error = rrl_excesswhitespace;
    21495e
    +    }
    21495e
    +
    21495e
    +    /* Advance protocol pointer over leading whitespace, NUL terminate the uri
    21495e
    +     * If non-SP whitespace is encountered, mark as specific error
    21495e
    +     */
    21495e
    +    for (r->protocol = ll; apr_isspace(*r->protocol); ++r->protocol)
    21495e
    +        if (*r->protocol != ' ' && deferred_error == rrl_none)
    21495e
    +            deferred_error = rrl_badwhitespace;
    21495e
    +    *ll = '\0';
    21495e
    +
    21495e
    +    /* Scan the protocol up to the next whitespace, validation comes later */
    21495e
    +    if (!(ll = (char*) ap_scan_vchar_obstext(r->protocol))) {
    21495e
    +        len = strlen(r->protocol);
    21495e
    +        goto rrl_done;
    21495e
    +    }
    21495e
    +    len = ll - r->protocol;
    21495e
    +
    21495e
    +    /* Advance over trailing whitespace, if found mark in error,
    21495e
    +     * determine if trailing text is found, unconditionally mark in error,
    21495e
    +     * finally NUL terminate the protocol string
    21495e
    +     */
    21495e
    +    if (*ll && !apr_isspace(*ll)) {
    21495e
    +        deferred_error = rrl_badprotocol;
    21495e
    +    }
    21495e
    +    else if (strict && *ll) {
    21495e
    +        deferred_error = rrl_excesswhitespace;
    21495e
    +    }
    21495e
    +    else {
    21495e
    +        for ( ; apr_isspace(*ll); ++ll)
    21495e
    +            if (*ll != ' ' && deferred_error == rrl_none)
    21495e
    +                deferred_error = rrl_badwhitespace;
    21495e
    +        if (*ll && deferred_error == rrl_none)
    21495e
    +            deferred_error = rrl_trailingtext;
    21495e
    +    }
    21495e
    +    *((char *)r->protocol + len) = '\0';
    21495e
    +
    21495e
    +rrl_done:
    21495e
    +    /* For internal integrety and palloc efficiency, reconstruct the_request
    21495e
    +     * in one palloc, using only single SP characters, per spec.
    21495e
    +     */
    21495e
    +    r->the_request = apr_pstrcat(r->pool, r->method, *uri ? " " : NULL, uri,
    21495e
    +                                 *r->protocol ? " " : NULL, r->protocol, NULL);
    21495e
    +
    21495e
    +    if (len == 8
    21495e
    +            && r->protocol[0] == 'H' && r->protocol[1] == 'T'
    21495e
    +            && r->protocol[2] == 'T' && r->protocol[3] == 'P'
    21495e
    +            && r->protocol[4] == '/' && apr_isdigit(r->protocol[5])
    21495e
    +            && r->protocol[6] == '.' && apr_isdigit(r->protocol[7])
    21495e
    +            && r->protocol[5] != '0') {
    21495e
    +        r->assbackwards = 0;
    21495e
    +        r->proto_num = HTTP_VERSION(r->protocol[5] - '0', r->protocol[7] - '0');
    21495e
    +    }
    21495e
    +    else if (len == 8
    21495e
    +                 && (r->protocol[0] == 'H' || r->protocol[0] == 'h')
    21495e
    +                 && (r->protocol[1] == 'T' || r->protocol[1] == 't')
    21495e
    +                 && (r->protocol[2] == 'T' || r->protocol[2] == 't')
    21495e
    +                 && (r->protocol[3] == 'P' || r->protocol[3] == 'p')
    21495e
    +                 && r->protocol[4] == '/' && apr_isdigit(r->protocol[5])
    21495e
    +                 && r->protocol[6] == '.' && apr_isdigit(r->protocol[7])
    21495e
    +                 && r->protocol[5] != '0') {
    21495e
             r->assbackwards = 0;
    21495e
    -        pro = ll;
    21495e
    -        len = strlen(ll);
    21495e
    -    } else {
    21495e
    +        r->proto_num = HTTP_VERSION(r->protocol[5] - '0', r->protocol[7] - '0');
    21495e
    +        if (strict && deferred_error == rrl_none)
    21495e
    +            deferred_error = rrl_badprotocol;
    21495e
    +        else
    21495e
    +            memcpy((char*)r->protocol, "HTTP", 4);
    21495e
    +    } else if (r->protocol[0]) {
    21495e
    +        r->proto_num = HTTP_VERSION(0, 9);
    21495e
    +        /* Defer setting the r->protocol string till error msg is composed */
    21495e
    +        if (deferred_error == rrl_none)
    21495e
    +            deferred_error = rrl_badprotocol;
    21495e
    +    }
    21495e
    +    else {
    21495e
             r->assbackwards = 1;
    21495e
    -        pro = "HTTP/0.9";
    21495e
    -        len = 8;
    21495e
    +        r->protocol  = apr_pstrdup(r->pool, "HTTP/0.9");
    21495e
    +        r->proto_num = HTTP_VERSION(0, 9);
    21495e
         }
    21495e
    -    r->protocol = apr_pstrmemdup(r->pool, pro, len);
    21495e
     
    21495e
    -    /* Avoid sscanf in the common case */
    21495e
    -    if (len == 8
    21495e
    -        && pro[0] == 'H' && pro[1] == 'T' && pro[2] == 'T' && pro[3] == 'P'
    21495e
    -        && pro[4] == '/' && apr_isdigit(pro[5]) && pro[6] == '.'
    21495e
    -        && apr_isdigit(pro[7])) {
    21495e
    -        r->proto_num = HTTP_VERSION(pro[5] - '0', pro[7] - '0');
    21495e
    -    }
    21495e
    -    else if (3 == sscanf(r->protocol, "%4s/%u.%u", http, &major, &minor)
    21495e
    -             && (strcasecmp("http", http) == 0)
    21495e
    -             && (minor < HTTP_VERSION(1, 0)) ) /* don't allow HTTP/0.1000 */
    21495e
    -        r->proto_num = HTTP_VERSION(major, minor);
    21495e
    -    else
    21495e
    -        r->proto_num = HTTP_VERSION(1, 0);
    21495e
    +    /* Determine the method_number and parse the uri prior to invoking error
    21495e
    +     * handling, such that these fields are available for subsitution
    21495e
    +     */
    21495e
    +    r->method_number = ap_method_number_of(r->method);
    21495e
    +    if (r->method_number == M_GET && r->method[0] == 'H')
    21495e
    +        r->header_only = 1;
    21495e
    +
    21495e
    +    ap_parse_uri(r, uri);
    21495e
    +
    21495e
    +    /* With the request understood, we can consider HTTP/0.9 specific errors */
    21495e
    +    if (r->proto_num == HTTP_VERSION(0, 9) && deferred_error == rrl_none) {
    21495e
    +        if (conf->http09_enable == AP_HTTP09_DISABLE)
    21495e
    +            deferred_error = rrl_reject09;
    21495e
    +        else if (strict && (r->method_number != M_GET || r->header_only))
    21495e
    +            deferred_error = rrl_badmethod09;
    21495e
    +    }
    21495e
    +
    21495e
    +    /* Now that the method, uri and protocol are all processed,
    21495e
    +     * we can safely resume any deferred error reporting
    21495e
    +     */
    21495e
    +    if (deferred_error != rrl_none) {
    21495e
    +        if (deferred_error == rrl_badmethod)
    21495e
    +            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03445)
    21495e
    +                          "HTTP Request Line; Invalid method token: '%.*s'",
    21495e
    +                          field_name_len(r->method), r->method);
    21495e
    +        else if (deferred_error == rrl_badmethod09)
    21495e
    +            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03444)
    21495e
    +                          "HTTP Request Line; Invalid method token: '%.*s'"
    21495e
    +                          " (only GET is allowed for HTTP/0.9 requests)",
    21495e
    +                          field_name_len(r->method), r->method);
    21495e
    +        else if (deferred_error == rrl_missinguri)
    21495e
    +            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03446)
    21495e
    +                          "HTTP Request Line; Missing URI");
    21495e
    +        else if (deferred_error == rrl_baduri)
    21495e
    +            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03454)
    21495e
    +                          "HTTP Request Line; URI incorrectly encoded: '%.*s'",
    21495e
    +                          field_name_len(r->uri), r->uri);
    21495e
    +        else if (deferred_error == rrl_badwhitespace)
    21495e
    +            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03447)
    21495e
    +                          "HTTP Request Line; Invalid whitespace");
    21495e
    +        else if (deferred_error == rrl_excesswhitespace)
    21495e
    +            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03448)
    21495e
    +                          "HTTP Request Line; Excess whitespace "
    21495e
    +                          "(disallowed by HttpProtocolOptions Strict");
    21495e
    +        else if (deferred_error == rrl_trailingtext)
    21495e
    +            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03449)
    21495e
    +                          "HTTP Request Line; Extraneous text found '%.*s' "
    21495e
    +                          "(perhaps whitespace was injected?)",
    21495e
    +                          field_name_len(ll), ll);
    21495e
    +        else if (deferred_error == rrl_reject09)
    21495e
    +            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02401)
    21495e
    +                          "HTTP Request Line; Rejected HTTP/0.9 request");
    21495e
    +        else if (deferred_error == rrl_badprotocol)
    21495e
    +            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02418)
    21495e
    +                          "HTTP Request Line; Unrecognized protocol '%.*s' "
    21495e
    +                          "(perhaps whitespace was injected?)",
    21495e
    +                          field_name_len(r->protocol), r->protocol);
    21495e
    +        r->status = HTTP_BAD_REQUEST;
    21495e
    +        goto rrl_failed;
    21495e
    +    }
    21495e
    +
    21495e
    +    if (conf->http_methods == AP_HTTP_METHODS_REGISTERED
    21495e
    +            && r->method_number == M_INVALID) {
    21495e
    +        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02423)
    21495e
    +                      "HTTP Request Line; Unrecognized HTTP method: '%.*s' "
    21495e
    +                      "(disallowed by RegisteredMethods)",
    21495e
    +                      field_name_len(r->method), r->method);
    21495e
    +        r->status = HTTP_NOT_IMPLEMENTED;
    21495e
    +        /* This can't happen in an HTTP/0.9 request, we verified GET above */
    21495e
    +        return 0;
    21495e
    +    }
    21495e
    +
    21495e
    +    if (r->status != HTTP_OK) {
    21495e
    +        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03450)
    21495e
    +                      "HTTP Request Line; Unable to parse URI: '%.*s'",
    21495e
    +                      field_name_len(r->uri), r->uri);
    21495e
    +        goto rrl_failed;
    21495e
    +    }
    21495e
    +
    21495e
    +    if (strict) {
    21495e
    +        if (r->parsed_uri.fragment) {
    21495e
    +            /* RFC3986 3.5: no fragment */
    21495e
    +            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02421)
    21495e
    +                          "HTTP Request Line; URI must not contain a fragment");
    21495e
    +            r->status = HTTP_BAD_REQUEST;
    21495e
    +            goto rrl_failed;
    21495e
    +        }
    21495e
    +        if (r->parsed_uri.user || r->parsed_uri.password) {
    21495e
    +            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02422)
    21495e
    +                          "HTTP Request Line; URI must not contain a "
    21495e
    +                          "username/password");
    21495e
    +            r->status = HTTP_BAD_REQUEST;
    21495e
    +            goto rrl_failed;
    21495e
    +        }
    21495e
    +    }
    21495e
     
    21495e
         return 1;
    21495e
    +rrl_failed:
    21495e
    +    if (r->proto_num == HTTP_VERSION(0, 9)) {
    21495e
    +        /* Send all parsing and protocol error response with 1.x behavior,
    21495e
    +         * and reserve 505 errors for actual HTTP protocols presented.
    21495e
    +         * As called out in RFC7230 3.5, any errors parsing the protocol
    21495e
    +         * from the request line are nearly always misencoded HTTP/1.x
    21495e
    +         * requests. Only a valid 0.9 request with no parsing errors
    21495e
    +         * at all may be treated as a simple request, if allowed.
    21495e
    +         */
    21495e
    +        r->assbackwards = 0;
    21495e
    +        r->connection->keepalive = AP_CONN_CLOSE;
    21495e
    +        r->proto_num = HTTP_VERSION(1, 0);
    21495e
    +        r->protocol  = apr_pstrdup(r->pool, "HTTP/1.0");
    21495e
    +    }
    21495e
    +    return 0;
    21495e
     }
    21495e
     
    21495e
     static int table_do_fn_check_lengths(void *r_, const char *key,
    21495e
    @@ -670,26 +916,13 @@
    21495e
     
    21495e
         r->status = HTTP_BAD_REQUEST;
    21495e
         apr_table_setn(r->notes, "error-notes",
    21495e
    -                   apr_pstrcat(r->pool, "Size of a request header field "
    21495e
    -                               "after merging exceeds server limit.
    "
    21495e
    -                               "\n
    \n",
    21495e
    -                               ap_escape_html(r->pool, key),
    21495e
    -                               "\n", NULL));
    21495e
    -    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00560) "Request header "
    21495e
    -                  "exceeds LimitRequestFieldSize after merging: %s", key);
    21495e
    +                   "Size of a request header field exceeds server limit.");
    21495e
    +    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00560) "Request "
    21495e
    +                  "header exceeds LimitRequestFieldSize after merging: %.*s",
    21495e
    +                  field_name_len(key), key);
    21495e
         return 0;
    21495e
     }
    21495e
     
    21495e
    -/* get the length of the field name for logging, but no more than 80 bytes */
    21495e
    -#define LOG_NAME_MAX_LEN 80
    21495e
    -static int field_name_len(const char *field)
    21495e
    -{
    21495e
    -    const char *end = ap_strchr_c(field, ':');
    21495e
    -    if (end == NULL || end - field > LOG_NAME_MAX_LEN)
    21495e
    -        return LOG_NAME_MAX_LEN;
    21495e
    -    return end - field;
    21495e
    -}
    21495e
    -
    21495e
     AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb)
    21495e
     {
    21495e
         char *last_field = NULL;
    21495e
    @@ -700,6 +933,8 @@
    21495e
         apr_size_t len;
    21495e
         int fields_read = 0;
    21495e
         char *tmp_field;
    21495e
    +    core_server_config *conf = ap_get_core_module_config(r->server->module_config);
    21495e
    +    int strict = (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE);
    21495e
     
    21495e
         /*
    21495e
          * Read header lines until we get the empty separator line, a read error,
    21495e
    @@ -707,11 +942,10 @@
    21495e
          */
    21495e
         while(1) {
    21495e
             apr_status_t rv;
    21495e
    -        int folded = 0;
    21495e
     
    21495e
             field = NULL;
    21495e
             rv = ap_rgetline(&field, r->server->limit_req_fieldsize + 2,
    21495e
    -                         &len, r, 0, bb);
    21495e
    +                         &len, r, strict ? AP_GETLINE_CRLF : 0, bb);
    21495e
     
    21495e
             if (rv != APR_SUCCESS) {
    21495e
                 if (APR_STATUS_IS_TIMEUP(rv)) {
    21495e
    @@ -728,153 +962,217 @@
    21495e
                  * exceeds the configured limit for a field size.
    21495e
                  */
    21495e
                 if (rv == APR_ENOSPC) {
    21495e
    -                const char *field_escaped;
    21495e
    -                if (field) {
    21495e
    -                    /* ensure ap_escape_html will terminate correctly */
    21495e
    -                    field[len - 1] = '\0';
    21495e
    -                    field_escaped = ap_escape_html(r->pool, field);
    21495e
    -                }
    21495e
    -                else {
    21495e
    -                    field_escaped = field = "";
    21495e
    -                }
    21495e
    -
    21495e
                     apr_table_setn(r->notes, "error-notes",
    21495e
    -                               apr_psprintf(r->pool,
    21495e
    -                                           "Size of a request header field "
    21495e
    -                                           "exceeds server limit.
    \n"
    21495e
    -                                           "
    \n%.*s\n
    \n",
    21495e
    -                                           field_name_len(field_escaped),
    21495e
    -                                           field_escaped));
    21495e
    +                               "Size of a request header field "
    21495e
    +                               "exceeds server limit.");
    21495e
                     ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00561)
    21495e
                                   "Request header exceeds LimitRequestFieldSize%s"
    21495e
                                   "%.*s",
    21495e
    -                              *field ? ": " : "",
    21495e
    -                              field_name_len(field), field);
    21495e
    +                              (field && *field) ? ": " : "",
    21495e
    +                              (field) ? field_name_len(field) : 0,
    21495e
    +                              (field) ? field : "");
    21495e
                 }
    21495e
                 return;
    21495e
             }
    21495e
     
    21495e
    -        if (last_field != NULL) {
    21495e
    -            if ((len > 0) && ((*field == '\t') || *field == ' ')) {
    21495e
    -                /* This line is a continuation of the preceding line(s),
    21495e
    -                 * so append it to the line that we've set aside.
    21495e
    -                 * Note: this uses a power-of-two allocator to avoid
    21495e
    -                 * doing O(n) allocs and using O(n^2) space for
    21495e
    -                 * continuations that span many many lines.
    21495e
    -                 */
    21495e
    -                apr_size_t fold_len = last_len + len + 1; /* trailing null */
    21495e
     
    21495e
    -                if (fold_len >= (apr_size_t)(r->server->limit_req_fieldsize)) {
    21495e
    -                    r->status = HTTP_BAD_REQUEST;
    21495e
    -                    /* report what we have accumulated so far before the
    21495e
    -                     * overflow (last_field) as the field with the problem
    21495e
    -                     */
    21495e
    -                    apr_table_setn(r->notes, "error-notes",
    21495e
    -                                   apr_psprintf(r->pool,
    21495e
    -                                               "Size of a request header field "
    21495e
    -                                               "after folding "
    21495e
    -                                               "exceeds server limit.
    \n"
    21495e
    -                                               "
    \n%.*s\n
    \n",
    21495e
    -                                               field_name_len(last_field), 
    21495e
    -                                               ap_escape_html(r->pool, last_field)));
    21495e
    -                    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00562)
    21495e
    -                                  "Request header exceeds LimitRequestFieldSize "
    21495e
    -                                  "after folding: %.*s",
    21495e
    -                                  field_name_len(last_field), last_field);
    21495e
    -                    return;
    21495e
    -                }
    21495e
    +        /* For all header values, and all obs-fold lines, the presence of
    21495e
    +         * additional whitespace is a no-op, so collapse trailing whitespace
    21495e
    +         * to save buffer allocation and optimize copy operations.
    21495e
    +         * Do not remove the last single whitespace under any condition.
    21495e
    +         */
    21495e
    +        while (len > 1 && (field[len-1] == '\t' || field[len-1] == ' ')) {
    21495e
    +            field[--len] = '\0';
    21495e
    +        }
    21495e
    +
    21495e
    +        if (*field == '\t' || *field == ' ') {
    21495e
    +            /* Append any newly-read obs-fold line onto the preceding
    21495e
    +             * last_field line we are processing
    21495e
    +             */
    21495e
    +            apr_size_t fold_len;
    21495e
     
    21495e
    +            if (last_field == NULL) {
    21495e
    +                r->status = HTTP_BAD_REQUEST;
    21495e
    +                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03442)
    21495e
    +                              "Line folding encountered before first"
    21495e
    +                              " header line");
    21495e
    +                return;
    21495e
    +            }
    21495e
    +
    21495e
    +            if (field[1] == '\0') {
    21495e
    +                r->status = HTTP_BAD_REQUEST;
    21495e
    +                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03443)
    21495e
    +                              "Empty folded line encountered");
    21495e
    +                return;
    21495e
    +            }
    21495e
    +
    21495e
    +            /* Leading whitespace on an obs-fold line can be
    21495e
    +             * similarly discarded */
    21495e
    +            while (field[1] == '\t' || field[1] == ' ') {
    21495e
    +                ++field; --len;
    21495e
    +            }
    21495e
    +
    21495e
    +            /* This line is a continuation of the preceding line(s),
    21495e
    +             * so append it to the line that we've set aside.
    21495e
    +             * Note: this uses a power-of-two allocator to avoid
    21495e
    +             * doing O(n) allocs and using O(n^2) space for
    21495e
    +             * continuations that span many many lines.
    21495e
    +             */
    21495e
    +            fold_len = last_len + len + 1; /* trailing null */
    21495e
    +
    21495e
    +            if (fold_len >= (apr_size_t)(r->server->limit_req_fieldsize)) {
    21495e
    +                r->status = HTTP_BAD_REQUEST;
    21495e
    +                /* report what we have accumulated so far before the
    21495e
    +                 * overflow (last_field) as the field with the problem
    21495e
    +                 */
    21495e
    +                apr_table_setn(r->notes, "error-notes",
    21495e
    +                               "Size of a request header field "
    21495e
    +                               "exceeds server limit.");
    21495e
    +                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00562)
    21495e
    +                              "Request header exceeds LimitRequestFieldSize "
    21495e
    +                              "after folding: %.*s",
    21495e
    +                              field_name_len(last_field), last_field);
    21495e
    +                return;
    21495e
    +            }
    21495e
    +
    21495e
    +            if (fold_len > alloc_len) {
    21495e
    +                char *fold_buf;
    21495e
    +                alloc_len += alloc_len;
    21495e
                     if (fold_len > alloc_len) {
    21495e
    -                    char *fold_buf;
    21495e
    -                    alloc_len += alloc_len;
    21495e
    -                    if (fold_len > alloc_len) {
    21495e
    -                        alloc_len = fold_len;
    21495e
    -                    }
    21495e
    -                    fold_buf = (char *)apr_palloc(r->pool, alloc_len);
    21495e
    -                    memcpy(fold_buf, last_field, last_len);
    21495e
    -                    last_field = fold_buf;
    21495e
    +                    alloc_len = fold_len;
    21495e
                     }
    21495e
    -                memcpy(last_field + last_len, field, len +1); /* +1 for nul */
    21495e
    -                last_len += len;
    21495e
    -                folded = 1;
    21495e
    -            }
    21495e
    -            else /* not a continuation line */ {
    21495e
    +                fold_buf = (char *)apr_palloc(r->pool, alloc_len);
    21495e
    +                memcpy(fold_buf, last_field, last_len);
    21495e
    +                last_field = fold_buf;
    21495e
    +            }
    21495e
    +            memcpy(last_field + last_len, field, len +1); /* +1 for nul */
    21495e
    +            /* Replace obs-fold w/ SP per RFC 7230 3.2.4 */
    21495e
    +            last_field[last_len] = ' ';
    21495e
    +            last_len += len;
    21495e
     
    21495e
    -                if (r->server->limit_req_fields
    21495e
    +            /* We've appended this obs-fold line to last_len, proceed to
    21495e
    +             * read the next input line
    21495e
    +             */
    21495e
    +            continue;
    21495e
    +        }
    21495e
    +        else if (last_field != NULL) {
    21495e
    +            /* Process the previous last_field header line with all obs-folded
    21495e
    +             * segments already concatinated (this is not operating on the
    21495e
    +             * most recently read input line).
    21495e
    +             */
    21495e
    +            if (r->server->limit_req_fields
    21495e
                         && (++fields_read > r->server->limit_req_fields)) {
    21495e
    -                    r->status = HTTP_BAD_REQUEST;
    21495e
    -                    apr_table_setn(r->notes, "error-notes",
    21495e
    -                                   "The number of request header fields "
    21495e
    -                                   "exceeds this server's limit.");
    21495e
    -                    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00563)
    21495e
    -                                  "Number of request headers exceeds "
    21495e
    -                                  "LimitRequestFields");
    21495e
    -                    return;
    21495e
    -                }
    21495e
    +                r->status = HTTP_BAD_REQUEST;
    21495e
    +                apr_table_setn(r->notes, "error-notes",
    21495e
    +                               "The number of request header fields "
    21495e
    +                               "exceeds this server's limit.");
    21495e
    +                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00563)
    21495e
    +                               "Number of request headers exceeds "
    21495e
    +                               "LimitRequestFields");
    21495e
    +                return;
    21495e
    +            }
    21495e
     
    21495e
    -                if (!(value = strchr(last_field, ':'))) { /* Find ':' or    */
    21495e
    -                    r->status = HTTP_BAD_REQUEST;      /* abort bad request */
    21495e
    -                    apr_table_setn(r->notes, "error-notes",
    21495e
    -                                   apr_psprintf(r->pool,
    21495e
    -                                               "Request header field is "
    21495e
    -                                               "missing ':' separator.
    \n"
    21495e
    -                                               "
    \n%.*s
    \n",
    21495e
    -                                               (int)LOG_NAME_MAX_LEN,
    21495e
    -                                               ap_escape_html(r->pool,
    21495e
    -                                                              last_field)));
    21495e
    -                    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00564)
    21495e
    +            if (!strict)
    21495e
    +            {
    21495e
    +                /* Not Strict ('Unsafe' mode), using the legacy parser */
    21495e
    +
    21495e
    +                if (!(value = strchr(last_field, ':'))) { /* Find ':' or */
    21495e
    +                    r->status = HTTP_BAD_REQUEST;   /* abort bad request */
    21495e
    +                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00564)
    21495e
                                       "Request header field is missing ':' "
    21495e
                                       "separator: %.*s", (int)LOG_NAME_MAX_LEN,
    21495e
                                       last_field);
    21495e
    +
    21495e
                         return;
    21495e
                     }
    21495e
     
    21495e
    -                tmp_field = value - 1; /* last character of field-name */
    21495e
    +                /* last character of field-name */
    21495e
    +                tmp_field = value - (value > last_field ? 1 : 0);
    21495e
     
    21495e
                     *value++ = '\0'; /* NUL-terminate at colon */
    21495e
     
    21495e
    +                if (strpbrk(last_field, "\t\n\v\f\r ")) {
    21495e
    +                    r->status = HTTP_BAD_REQUEST;
    21495e
    +                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03452)
    21495e
    +                                  "Request header field name presented"
    21495e
    +                                  " invalid whitespace");
    21495e
    +                    return;
    21495e
    +                }
    21495e
    +
    21495e
                     while (*value == ' ' || *value == '\t') {
    21495e
    -                    ++value;            /* Skip to start of value   */
    21495e
    +                     ++value;            /* Skip to start of value   */
    21495e
    +                }
    21495e
    +
    21495e
    +                if (strpbrk(value, "\n\v\f\r")) {
    21495e
    +                    r->status = HTTP_BAD_REQUEST;
    21495e
    +                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03451)
    21495e
    +                                  "Request header field value presented"
    21495e
    +                                  " bad whitespace");
    21495e
    +                    return;
    21495e
                     }
    21495e
     
    21495e
    -                /* Strip LWS after field-name: */
    21495e
    -                while (tmp_field > last_field
    21495e
    -                       && (*tmp_field == ' ' || *tmp_field == '\t')) {
    21495e
    -                    *tmp_field-- = '\0';
    21495e
    +                if (tmp_field == last_field) {
    21495e
    +                    r->status = HTTP_BAD_REQUEST;
    21495e
    +                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03453)
    21495e
    +                                  "Request header field name was empty");
    21495e
    +                    return;
    21495e
    +                }
    21495e
    +            }
    21495e
    +            else /* Using strict RFC7230 parsing */
    21495e
    +            {
    21495e
    +                /* Ensure valid token chars before ':' per RFC 7230 3.2.4 */
    21495e
    +                value = (char *)ap_scan_http_token(last_field);
    21495e
    +                if ((value == last_field) || *value != ':') {
    21495e
    +                    r->status = HTTP_BAD_REQUEST;
    21495e
    +                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02426)
    21495e
    +                                  "Request header field name is malformed: "
    21495e
    +                                  "%.*s", (int)LOG_NAME_MAX_LEN, last_field);
    21495e
    +                    return;
    21495e
                     }
    21495e
     
    21495e
    -                /* Strip LWS after field-value: */
    21495e
    -                tmp_field = last_field + last_len - 1;
    21495e
    -                while (tmp_field > value
    21495e
    -                       && (*tmp_field == ' ' || *tmp_field == '\t')) {
    21495e
    -                    *tmp_field-- = '\0';
    21495e
    +                *value++ = '\0'; /* NUL-terminate last_field name at ':' */
    21495e
    +
    21495e
    +                while (*value == ' ' || *value == '\t') {
    21495e
    +                    ++value;     /* Skip LWS of value */
    21495e
                     }
    21495e
     
    21495e
    -                apr_table_addn(r->headers_in, last_field, value);
    21495e
    +                /* Find invalid, non-HT ctrl char, or the trailing NULL */
    21495e
    +                tmp_field = (char *)ap_scan_http_field_content(value);
    21495e
     
    21495e
    -                /* reset the alloc_len so that we'll allocate a new
    21495e
    -                 * buffer if we have to do any more folding: we can't
    21495e
    -                 * use the previous buffer because its contents are
    21495e
    -                 * now part of r->headers_in
    21495e
    +                /* Reject value for all garbage input (CTRLs excluding HT)
    21495e
    +                 * e.g. only VCHAR / SP / HT / obs-text are allowed per
    21495e
    +                 * RFC7230 3.2.6 - leave all more explicit rule enforcement
    21495e
    +                 * for specific header handler logic later in the cycle
    21495e
                      */
    21495e
    -                alloc_len = 0;
    21495e
    +                if (*tmp_field != '\0') {
    21495e
    +                    r->status = HTTP_BAD_REQUEST;
    21495e
    +                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02427)
    21495e
    +                                  "Request header value is malformed: "
    21495e
    +                                  "%.*s", (int)LOG_NAME_MAX_LEN, value);
    21495e
    +                    return;
    21495e
    +                }
    21495e
    +            }
    21495e
    +
    21495e
    +            apr_table_addn(r->headers_in, last_field, value);
    21495e
     
    21495e
    -            } /* end if current line is not a continuation starting with tab */
    21495e
    +            /* This last_field header is now stored in headers_in,
    21495e
    +             * resume processing of the current input line.
    21495e
    +             */
    21495e
             }
    21495e
     
    21495e
    -        /* Found a blank line, stop. */
    21495e
    +        /* Found the terminating empty end-of-headers line, stop. */
    21495e
             if (len == 0) {
    21495e
                 break;
    21495e
             }
    21495e
     
    21495e
    -        /* Keep track of this line so that we can parse it on
    21495e
    -         * the next loop iteration.  (In the folded case, last_field
    21495e
    -         * has been updated already.)
    21495e
    +        /* Keep track of this new header line so that we can extend it across
    21495e
    +         * any obs-fold or parse it on the next loop iteration. We referenced
    21495e
    +         * our previously allocated buffer in r->headers_in,
    21495e
    +         * so allocate a fresh buffer if required.
    21495e
              */
    21495e
    -        if (!folded) {
    21495e
    -            last_field = field;
    21495e
    -            last_len = len;
    21495e
    -        }
    21495e
    +        alloc_len = 0;
    21495e
    +        last_field = field;
    21495e
    +        last_len = len;
    21495e
         }
    21495e
     
    21495e
         /* Combine multiple message-header fields with the same
    21495e
    @@ -899,7 +1197,7 @@
    21495e
         request_rec *r;
    21495e
         apr_pool_t *p;
    21495e
         const char *expect;
    21495e
    -    int access_status = HTTP_OK;
    21495e
    +    int access_status;
    21495e
         apr_bucket_brigade *tmp_bb;
    21495e
         apr_socket_t *csd;
    21495e
         apr_interval_time_t cur_timeout;
    21495e
    @@ -958,35 +1256,39 @@
    21495e
     
    21495e
         /* Get the request... */
    21495e
         if (!read_request_line(r, tmp_bb)) {
    21495e
    -        if (r->status == HTTP_REQUEST_URI_TOO_LARGE
    21495e
    -            || r->status == HTTP_BAD_REQUEST) {
    21495e
    +        switch (r->status) {
    21495e
    +        case HTTP_REQUEST_URI_TOO_LARGE:
    21495e
    +        case HTTP_BAD_REQUEST:
    21495e
    +        case HTTP_VERSION_NOT_SUPPORTED:
    21495e
    +        case HTTP_NOT_IMPLEMENTED:
    21495e
                 if (r->status == HTTP_REQUEST_URI_TOO_LARGE) {
    21495e
                     ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00565)
    21495e
                                   "request failed: client's request-line exceeds LimitRequestLine (longer than %d)",
    21495e
                                   r->server->limit_req_line);
    21495e
                 }
    21495e
                 else if (r->method == NULL) {
    21495e
    -                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00566)
    21495e
    -                              "request failed: invalid characters in URI");
    21495e
    +                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00566)
    21495e
    +                              "request failed: malformed request line");
    21495e
                 }
    21495e
    -            ap_send_error_response(r, 0);
    21495e
    +            access_status = r->status;
    21495e
    +            r->status = HTTP_OK;
    21495e
    +            ap_die(access_status, r);
    21495e
                 ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
    21495e
                 ap_run_log_transaction(r);
    21495e
    +            r = NULL;
    21495e
                 apr_brigade_destroy(tmp_bb);
    21495e
                 goto traceout;
    21495e
    -        }
    21495e
    -        else if (r->status == HTTP_REQUEST_TIME_OUT) {
    21495e
    -            ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
    21495e
    -            if (!r->connection->keepalives) {
    21495e
    +        case HTTP_REQUEST_TIME_OUT:
    21495e
    +            ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, NULL);
    21495e
    +            if (!r->connection->keepalives)
    21495e
                     ap_run_log_transaction(r);
    21495e
    -            }
    21495e
                 apr_brigade_destroy(tmp_bb);
    21495e
                 goto traceout;
    21495e
    +        default:
    21495e
    +            apr_brigade_destroy(tmp_bb);
    21495e
    +            r = NULL;
    21495e
    +            goto traceout;
    21495e
             }
    21495e
    -
    21495e
    -        apr_brigade_destroy(tmp_bb);
    21495e
    -        r = NULL;
    21495e
    -        goto traceout;
    21495e
         }
    21495e
     
    21495e
         /* We may have been in keep_alive_timeout mode, so toggle back
    21495e
    @@ -1003,7 +1305,7 @@
    21495e
         if (!r->assbackwards) {
    21495e
             ap_get_mime_headers_core(r, tmp_bb);
    21495e
             if (r->status != HTTP_OK) {
    21495e
    -            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00567)
    21495e
    +            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00567)
    21495e
                               "request failed: error reading the headers");
    21495e
                 ap_send_error_response(r, 0);
    21495e
                 ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
    21495e
    @@ -1021,25 +1323,6 @@
    21495e
                 apr_table_unset(r->headers_in, "Content-Length");
    21495e
             }
    21495e
         }
    21495e
    -    else {
    21495e
    -        if (r->header_only) {
    21495e
    -            /*
    21495e
    -             * Client asked for headers only with HTTP/0.9, which doesn't send
    21495e
    -             * headers! Have to dink things just to make sure the error message
    21495e
    -             * comes through...
    21495e
    -             */
    21495e
    -            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00568)
    21495e
    -                          "client sent invalid HTTP/0.9 request: HEAD %s",
    21495e
    -                          r->uri);
    21495e
    -            r->header_only = 0;
    21495e
    -            r->status = HTTP_BAD_REQUEST;
    21495e
    -            ap_send_error_response(r, 0);
    21495e
    -            ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
    21495e
    -            ap_run_log_transaction(r);
    21495e
    -            apr_brigade_destroy(tmp_bb);
    21495e
    -            goto traceout;
    21495e
    -        }
    21495e
    -    }
    21495e
     
    21495e
         apr_brigade_destroy(tmp_bb);
    21495e
     
    21495e
    @@ -1071,7 +1354,7 @@
    21495e
              * a Host: header, and the server MUST respond with 400 if it doesn't.
    21495e
              */
    21495e
             access_status = HTTP_BAD_REQUEST;
    21495e
    -        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00569)
    21495e
    +        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00569)
    21495e
                           "client sent HTTP/1.1 request without hostname "
    21495e
                           "(see RFC2616 section 14.23): %s", r->uri);
    21495e
         }
    21495e
    diff -uap httpd-2.4.6/server/util.c.cve8743 httpd-2.4.6/server/util.c
    21495e
    --- httpd-2.4.6/server/util.c.cve8743
    21495e
    +++ httpd-2.4.6/server/util.c
    21495e
    @@ -79,7 +79,7 @@
    21495e
      * char in here and get it to work, because if char is signed then it
    21495e
      * will first be sign extended.
    21495e
      */
    21495e
    -#define TEST_CHAR(c, f)        (test_char_table[(unsigned)(c)] & (f))
    21495e
    +#define TEST_CHAR(c, f)        (test_char_table[(unsigned char)(c)] & (f))
    21495e
     
    21495e
     /* Win32/NetWare/OS2 need to check for both forward and back slashes
    21495e
      * in ap_getparents() and ap_escape_url.
    21495e
    @@ -1449,6 +1449,37 @@
    21495e
         return find_list_item(p, line, tok, AP_ETAG_WEAK);
    21495e
     }
    21495e
     
    21495e
    +/* Scan a string for HTTP VCHAR/obs-text characters including HT and SP
    21495e
    + * (as used in header values, for example, in RFC 7230 section 3.2)
    21495e
    + * returning the pointer to the first non-HT ASCII ctrl character.
    21495e
    + */
    21495e
    +AP_DECLARE(const char *) ap_scan_http_field_content(const char *ptr)
    21495e
    +{
    21495e
    +    for ( ; !TEST_CHAR(*ptr, T_HTTP_CTRLS); ++ptr) ;
    21495e
    +
    21495e
    +    return ptr;
    21495e
    +}
    21495e
    +
    21495e
    +/* Scan a string for HTTP token characters, returning the pointer to
    21495e
    + * the first non-token character.
    21495e
    + */
    21495e
    +AP_DECLARE(const char *) ap_scan_http_token(const char *ptr)
    21495e
    +{
    21495e
    +    for ( ; !TEST_CHAR(*ptr, T_HTTP_TOKEN_STOP); ++ptr) ;
    21495e
    +
    21495e
    +    return ptr;
    21495e
    +}
    21495e
    +
    21495e
    +/* Scan a string for visible ASCII (0x21-0x7E) or obstext (0x80+)
    21495e
    + * and return a pointer to the first ctrl/space character encountered.
    21495e
    + */
    21495e
    +AP_DECLARE(const char *) ap_scan_vchar_obstext(const char *ptr)
    21495e
    +{
    21495e
    +    for ( ; TEST_CHAR(*ptr, T_VCHAR_OBSTEXT); ++ptr) ;
    21495e
    +
    21495e
    +    return ptr;
    21495e
    +}
    21495e
    +
    21495e
     /* Retrieve a token, spacing over it and returning a pointer to
    21495e
      * the first non-white byte afterwards.  Note that these tokens
    21495e
      * are delimited by semis and commas; and can also be delimited
    21495e
    diff -uap httpd-2.4.6/server/vhost.c.cve8743 httpd-2.4.6/server/vhost.c
    21495e
    --- httpd-2.4.6/server/vhost.c.cve8743
    21495e
    +++ httpd-2.4.6/server/vhost.c
    21495e
    @@ -685,6 +685,116 @@
    21495e
      * run-time vhost matching functions
    21495e
      */
    21495e
     
    21495e
    +static apr_status_t fix_hostname_v6_literal(request_rec *r, char *host)
    21495e
    +{
    21495e
    +    char *dst;
    21495e
    +    int double_colon = 0;
    21495e
    +
    21495e
    +    for (dst = host; *dst; dst++) {
    21495e
    +        if (apr_isxdigit(*dst)) {
    21495e
    +            if (apr_isupper(*dst)) {
    21495e
    +                *dst = apr_tolower(*dst);
    21495e
    +            }
    21495e
    +        }
    21495e
    +        else if (*dst == ':') {
    21495e
    +            if (*(dst + 1) == ':') {
    21495e
    +                if (double_colon)
    21495e
    +                    return APR_EINVAL;
    21495e
    +                double_colon = 1;
    21495e
    +            }
    21495e
    +            else if (*(dst + 1) == '.') {
    21495e
    +                return APR_EINVAL;
    21495e
    +            }
    21495e
    +        }
    21495e
    +        else if (*dst == '.') {
    21495e
    +            /* For IPv4-mapped IPv6 addresses like ::FFFF:129.144.52.38 */
    21495e
    +            if (*(dst + 1) == ':' || *(dst + 1) == '.')
    21495e
    +                return APR_EINVAL;
    21495e
    +        }
    21495e
    +        else {
    21495e
    +            return APR_EINVAL;
    21495e
    +        }
    21495e
    +    }
    21495e
    +    return APR_SUCCESS;
    21495e
    +}
    21495e
    +
    21495e
    +static apr_status_t fix_hostname_non_v6(request_rec *r, char *host)
    21495e
    +{
    21495e
    +    char *dst;
    21495e
    +
    21495e
    +    for (dst = host; *dst; dst++) {
    21495e
    +        if (apr_islower(*dst)) {
    21495e
    +            /* leave char unchanged */
    21495e
    +        }
    21495e
    +        else if (*dst == '.') {
    21495e
    +            if (*(dst + 1) == '.') {
    21495e
    +                return APR_EINVAL;
    21495e
    +            }
    21495e
    +        }
    21495e
    +        else if (apr_isupper(*dst)) {
    21495e
    +            *dst = apr_tolower(*dst);
    21495e
    +        }
    21495e
    +        else if (*dst == '/' || *dst == '\\') {
    21495e
    +            return APR_EINVAL;
    21495e
    +        }
    21495e
    +    }
    21495e
    +    /* strip trailing gubbins */
    21495e
    +    if (dst > host && dst[-1] == '.') {
    21495e
    +        dst[-1] = '\0';
    21495e
    +    }
    21495e
    +    return APR_SUCCESS;
    21495e
    +}
    21495e
    +
    21495e
    +/*
    21495e
    + * If strict mode ever becomes the default, this should be folded into
    21495e
    + * fix_hostname_non_v6()
    21495e
    + */
    21495e
    +static apr_status_t strict_hostname_check(request_rec *r, char *host)
    21495e
    +{
    21495e
    +    char *ch;
    21495e
    +    int is_dotted_decimal = 1, leading_zeroes = 0, dots = 0;
    21495e
    +
    21495e
    +    for (ch = host; *ch; ch++) {
    21495e
    +        if (!apr_isascii(*ch)) {
    21495e
    +            goto bad;
    21495e
    +        }
    21495e
    +        else if (apr_isalpha(*ch) || *ch == '-') {
    21495e
    +            is_dotted_decimal = 0;
    21495e
    +        }
    21495e
    +        else if (ch[0] == '.') {
    21495e
    +            dots++;
    21495e
    +            if (ch[1] == '0' && apr_isdigit(ch[2]))
    21495e
    +                leading_zeroes = 1;
    21495e
    +        }
    21495e
    +        else if (!apr_isdigit(*ch)) {
    21495e
    +           /* also takes care of multiple Host headers by denying commas */
    21495e
    +            goto bad;
    21495e
    +        }
    21495e
    +    }
    21495e
    +    if (is_dotted_decimal) {
    21495e
    +        if (host[0] == '.' || (host[0] == '0' && apr_isdigit(host[1])))
    21495e
    +            leading_zeroes = 1;
    21495e
    +        if (leading_zeroes || dots != 3) {
    21495e
    +            /* RFC 3986 7.4 */
    21495e
    +            goto bad;
    21495e
    +        }
    21495e
    +    }
    21495e
    +    else {
    21495e
    +        /* The top-level domain must start with a letter (RFC 1123 2.1) */
    21495e
    +        while (ch > host && *ch != '.')
    21495e
    +            ch--;
    21495e
    +        if (ch[0] == '.' && ch[1] != '\0' && !apr_isalpha(ch[1]))
    21495e
    +            goto bad;
    21495e
    +    }
    21495e
    +    return APR_SUCCESS;
    21495e
    +
    21495e
    +bad:
    21495e
    +    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02415)
    21495e
    +                  "[strict] Invalid host name '%s'%s%.6s",
    21495e
    +                  host, *ch ? ", problem near: " : "", ch);
    21495e
    +    return APR_EINVAL;
    21495e
    +}
    21495e
    +
    21495e
     /* Lowercase and remove any trailing dot and/or :port from the hostname,
    21495e
      * and check that it is sane.
    21495e
      *
    21495e
    @@ -698,79 +808,90 @@
    21495e
      * Instead we just check for filesystem metacharacters: directory
    21495e
      * separators / and \ and sequences of more than one dot.
    21495e
      */
    21495e
    -static void fix_hostname(request_rec *r)
    21495e
    +static int fix_hostname(request_rec *r, const char *host_header,
    21495e
    +                        unsigned http_conformance)
    21495e
     {
    21495e
    +    const char *src;
    21495e
         char *host, *scope_id;
    21495e
    -    char *dst;
    21495e
         apr_port_t port;
    21495e
         apr_status_t rv;
    21495e
         const char *c;
    21495e
    +    int is_v6literal = 0;
    21495e
    +    int strict = (http_conformance != AP_HTTP_CONFORMANCE_UNSAFE);
    21495e
     
    21495e
    -    /* According to RFC 2616, Host header field CAN be blank. */
    21495e
    -    if (!*r->hostname) {
    21495e
    -        return;
    21495e
    +    src = host_header ? host_header : r->hostname;
    21495e
    +
    21495e
    +    /* According to RFC 2616, Host header field CAN be blank */
    21495e
    +    if (!*src) {
    21495e
    +        return is_v6literal;
    21495e
         }
    21495e
     
    21495e
         /* apr_parse_addr_port will interpret a bare integer as a port
    21495e
          * which is incorrect in this context.  So treat it separately.
    21495e
          */
    21495e
    -    for (c = r->hostname; apr_isdigit(*c); ++c);
    21495e
    -    if (!*c) {  /* pure integer */
    21495e
    -        return;
    21495e
    +    for (c = src; apr_isdigit(*c); ++c);
    21495e
    +    if (!*c) {
    21495e
    +        /* pure integer */
    21495e
    +        if (strict) {
    21495e
    +            /* RFC 3986 7.4 */
    21495e
    +            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02416)
    21495e
    +                         "[strict] purely numeric host names not allowed: %s",
    21495e
    +                         src);
    21495e
    +            goto bad_nolog;
    21495e
    +        }
    21495e
    +        r->hostname = src;
    21495e
    +        return is_v6literal;
    21495e
    +    }
    21495e
    +
    21495e
    +    if (host_header) {
    21495e
    +        rv = apr_parse_addr_port(&host, &scope_id, &port, src, r->pool);
    21495e
    +        if (rv != APR_SUCCESS || scope_id)
    21495e
    +            goto bad;
    21495e
    +        if (port) {
    21495e
    +            /* Don't throw the Host: header's port number away:
    21495e
    +               save it in parsed_uri -- ap_get_server_port() needs it! */
    21495e
    +            /* @@@ XXX there should be a better way to pass the port.
    21495e
    +             *         Like r->hostname, there should be a r->portno
    21495e
    +             */
    21495e
    +            r->parsed_uri.port = port;
    21495e
    +            r->parsed_uri.port_str = apr_itoa(r->pool, (int)port);
    21495e
    +        }
    21495e
    +        if (host_header[0] == '[')
    21495e
    +            is_v6literal = 1;
    21495e
    +    }
    21495e
    +    else {
    21495e
    +        /*
    21495e
    +         * Already parsed, surrounding [ ] (if IPv6 literal) and :port have
    21495e
    +         * already been removed.
    21495e
    +         */
    21495e
    +        host = apr_pstrdup(r->pool, r->hostname);
    21495e
    +        if (ap_strchr(host, ':') != NULL)
    21495e
    +            is_v6literal = 1;
    21495e
         }
    21495e
     
    21495e
    -    rv = apr_parse_addr_port(&host, &scope_id, &port, r->hostname, r->pool);
    21495e
    -    if (rv != APR_SUCCESS || scope_id) {
    21495e
    -        goto bad;
    21495e
    +    if (is_v6literal) {
    21495e
    +        rv = fix_hostname_v6_literal(r, host);
    21495e
         }
    21495e
    -
    21495e
    -    if (port) {
    21495e
    -        /* Don't throw the Host: header's port number away:
    21495e
    -           save it in parsed_uri -- ap_get_server_port() needs it! */
    21495e
    -        /* @@@ XXX there should be a better way to pass the port.
    21495e
    -         *         Like r->hostname, there should be a r->portno
    21495e
    -         */
    21495e
    -        r->parsed_uri.port = port;
    21495e
    -        r->parsed_uri.port_str = apr_itoa(r->pool, (int)port);
    21495e
    +    else {
    21495e
    +        rv = fix_hostname_non_v6(r, host);
    21495e
    +        if (strict && rv == APR_SUCCESS)
    21495e
    +            rv = strict_hostname_check(r, host);
    21495e
         }
    21495e
    +    if (rv != APR_SUCCESS)
    21495e
    +        goto bad;
    21495e
     
    21495e
    -    /* if the hostname is an IPv6 numeric address string, it was validated
    21495e
    -     * already; otherwise, further validation is needed
    21495e
    -     */
    21495e
    -    if (r->hostname[0] != '[') {
    21495e
    -        for (dst = host; *dst; dst++) {
    21495e
    -            if (apr_islower(*dst)) {
    21495e