Description: | Contains directives that apply only if a condition is
+@@ -3541,6 +3611,23 @@
+
+
+
++
++
++ HTTP Methods that are not conforming to the relvant RFCs are normally
++rejected by request processing in Apache HTTPD. To avoid this, modules
++can register non-standard HTTP methods they support.
++The RegisterHttpMethod allows to register such
++methods manually. This can be useful for if such methods are forwared
++for external processing, e.g. to a CGI script.
++
++
++
+
+
+ Description: | Limits the CPU consumption of processes launched
+diff -uap httpd-2.4.6/include/http_core.h.cve8743 httpd-2.4.6/include/http_core.h
+--- httpd-2.4.6/include/http_core.h.cve8743
++++ httpd-2.4.6/include/http_core.h
+@@ -668,6 +668,21 @@
+ #define AP_MERGE_TRAILERS_DISABLE 2
+ int merge_trailers;
+
++#define AP_HTTP09_UNSET 0
++#define AP_HTTP09_ENABLE 1
++#define AP_HTTP09_DISABLE 2
++ char http09_enable;
++
++#define AP_HTTP_CONFORMANCE_UNSET 0
++#define AP_HTTP_CONFORMANCE_UNSAFE 1
++#define AP_HTTP_CONFORMANCE_STRICT 2
++ char http_conformance;
++
++#define AP_HTTP_METHODS_UNSET 0
++#define AP_HTTP_METHODS_LENIENT 1
++#define AP_HTTP_METHODS_REGISTERED 2
++ char http_methods;
++
+ } core_server_config;
+
+ /* for AddOutputFiltersByType in core.c */
+diff -uap httpd-2.4.6/include/httpd.h.cve8743 httpd-2.4.6/include/httpd.h
+--- httpd-2.4.6/include/httpd.h.cve8743
++++ httpd-2.4.6/include/httpd.h
+@@ -1584,6 +1584,28 @@
+ */
+ AP_DECLARE(int) ap_unescape_url(char *url);
+
++/* Scan a string for field content chars, as defined by RFC7230 section 3.2
++ * including VCHAR/obs-text, as well as HT and SP
++ * @param ptr The string to scan
++ * @return A pointer to the first (non-HT) ASCII ctrl character.
++ * @note lws and trailing whitespace are scanned, the caller is responsible
++ * for trimming leading and trailing whitespace
++ */
++AP_DECLARE(const char *) ap_scan_http_field_content(const char *ptr);
++
++/* Scan a string for token characters, as defined by RFC7230 section 3.2.6
++ * @param ptr The string to scan
++ * @return A pointer to the first non-token character.
++ */
++AP_DECLARE(const char *) ap_scan_http_token(const char *ptr);
++
++/* Scan a string for visible ASCII (0x21-0x7E) or obstext (0x80+)
++ * and return a pointer to the first SP/CTL/NUL character encountered.
++ * @param ptr The string to scan
++ * @return A pointer to the first SP/CTL character.
++ */
++AP_DECLARE(const char *) ap_scan_vchar_obstext(const char *ptr);
++
+ /**
+ * Unescape a URL, but leaving %2f (slashes) escaped
+ * @param url The url to unescape
+diff -uap httpd-2.4.6/include/http_protocol.h.cve8743 httpd-2.4.6/include/http_protocol.h
+--- httpd-2.4.6/include/http_protocol.h.cve8743
++++ httpd-2.4.6/include/http_protocol.h
+@@ -582,17 +582,22 @@
+ */
+ AP_CORE_DECLARE(void) ap_parse_uri(request_rec *r, const char *uri);
+
++#define AP_GETLINE_FOLD 1 /* Whether to merge continuation lines */
++#define AP_GETLINE_CRLF 2 /*Whether line ends must be in the form CR LF */
++
+ /**
+ * Get the next line of input for the request
+ * @param s The buffer into which to read the line
+ * @param n The size of the buffer
+ * @param r The request
+- * @param fold Whether to merge continuation lines
++ * @param flags Bit flag of multiple parsing options
++ * AP_GETLINE_FOLD Whether to merge continuation lines
++ * AP_GETLINE_CRLF Whether line ends must be in the form CR LF
+ * @return The length of the line, if successful
+ * n, if the line is too big to fit in the buffer
+ * -1 for miscellaneous errors
+ */
+-AP_DECLARE(int) ap_getline(char *s, int n, request_rec *r, int fold);
++AP_DECLARE(int) ap_getline(char *s, int n, request_rec *r, int flags);
+
+ /**
+ * Get the next line of input for the request
+@@ -610,7 +615,9 @@
+ * @param n The size of the buffer
+ * @param read The length of the line.
+ * @param r The request
+- * @param fold Whether to merge continuation lines
++ * @param flags Bit flag of multiple parsing options
++ * AP_GETLINE_FOLD Whether to merge continuation lines
++ * AP_GETLINE_CRLF Whether line ends must be in the form CR LF
+ * @param bb Working brigade to use when reading buckets
+ * @return APR_SUCCESS, if successful
+ * APR_ENOSPC, if the line is too big to fit in the buffer
+@@ -619,7 +626,7 @@
+ #if APR_CHARSET_EBCDIC
+ AP_DECLARE(apr_status_t) ap_rgetline(char **s, apr_size_t n,
+ apr_size_t *read,
+- request_rec *r, int fold,
++ request_rec *r, int flags,
+ apr_bucket_brigade *bb);
+ #else /* ASCII box */
+ #define ap_rgetline(s, n, read, r, fold, bb) \
+@@ -629,7 +636,7 @@
+ /** @see ap_rgetline */
+ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
+ apr_size_t *read,
+- request_rec *r, int fold,
++ request_rec *r, int flags,
+ apr_bucket_brigade *bb);
+
+ /**
+diff -uap httpd-2.4.6/modules/http/http_filters.c.cve8743 httpd-2.4.6/modules/http/http_filters.c
+--- httpd-2.4.6/modules/http/http_filters.c.cve8743
++++ httpd-2.4.6/modules/http/http_filters.c
+@@ -126,14 +126,15 @@
+
+ /**
+ * Parse a chunk line with optional extension, detect overflow.
+- * There are two error cases:
+- * 1) If the conversion would require too many bits, APR_EGENERAL is returned.
+- * 2) If the conversion used the correct number of bits, but an overflow
++ * There are several error cases:
++ * 1) If the chunk link is misformatted, APR_EINVAL is returned.
++ * 2) If the conversion would require too many bits, APR_EGENERAL is returned.
++ * 3) If the conversion used the correct number of bits, but an overflow
+ * caused only the sign bit to flip, then APR_ENOSPC is returned.
+- * In general, any negative number can be considered an overflow error.
++ * A negative chunk length always indicates an overflow error.
+ */
+ static apr_status_t parse_chunk_size(http_ctx_t *ctx, const char *buffer,
+- apr_size_t len, int linelimit)
++ apr_size_t len, int linelimit, int strict)
+ {
+ apr_size_t i = 0;
+
+@@ -146,6 +147,12 @@
+ if (ctx->state == BODY_CHUNK_END
+ || ctx->state == BODY_CHUNK_END_LF) {
+ if (c == LF) {
++ if (strict && (ctx->state != BODY_CHUNK_END_LF)) {
++ /*
++ * CR missing before LF.
++ */
++ return APR_EINVAL;
++ }
+ ctx->state = BODY_CHUNK;
+ }
+ else if (c == CR && ctx->state == BODY_CHUNK_END) {
+@@ -153,7 +160,7 @@
+ }
+ else {
+ /*
+- * LF expected.
++ * CRLF expected.
+ */
+ return APR_EINVAL;
+ }
+@@ -180,6 +187,12 @@
+ }
+
+ if (c == LF) {
++ if (strict && (ctx->state != BODY_CHUNK_LF)) {
++ /*
++ * CR missing before LF.
++ */
++ return APR_EINVAL;
++ }
+ if (ctx->remaining) {
+ ctx->state = BODY_CHUNK_DATA;
+ }
+@@ -201,14 +214,17 @@
+ }
+ else if (ctx->state == BODY_CHUNK_EXT) {
+ /*
+- * Control chars (but tabs) are invalid.
++ * Control chars (excluding tabs) are invalid.
++ * TODO: more precisely limit input
+ */
+ if (c != '\t' && apr_iscntrl(c)) {
+ return APR_EINVAL;
+ }
+ }
+ else if (c == ' ' || c == '\t') {
+- /* Be lenient up to 10 BWS (term from rfc7230 - 3.2.3).
++ /* Be lenient up to 10 implied *LWS, a legacy of RFC 2616,
++ * and noted as errata to RFC7230;
++ * https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4667
+ */
+ ctx->state = BODY_CHUNK_CR;
+ if (++ctx->chunk_bws > 10) {
+@@ -324,7 +340,10 @@
+ ap_input_mode_t mode, apr_read_type_e block,
+ apr_off_t readbytes)
+ {
+- core_server_config *conf;
++ core_server_config *conf =
++ (core_server_config *) ap_get_module_config(f->r->server->module_config,
++ &core_module);
++ int strict = (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE);
+ apr_bucket *e;
+ http_ctx_t *ctx = f->ctx;
+ apr_status_t rv;
+@@ -332,9 +351,6 @@
+ apr_bucket_brigade *bb;
+ int again;
+
+- conf = (core_server_config *)
+- ap_get_module_config(f->r->server->module_config, &core_module);
+-
+ /* just get out of the way of things we don't want. */
+ if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE) {
+ return ap_get_brigade(f->next, b, mode, block, readbytes);
+@@ -526,7 +542,7 @@
+ if (rv == APR_SUCCESS) {
+ parsing = 1;
+ rv = parse_chunk_size(ctx, buffer, len,
+- f->r->server->limit_req_fieldsize);
++ f->r->server->limit_req_fieldsize, strict);
+ }
+ if (rv != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, rv, f->r, APLOGNO(01590)
+@@ -668,14 +684,121 @@
+ return APR_SUCCESS;
+ }
+
++struct check_header_ctx {
++ request_rec *r;
++ int strict;
++};
++
++/* check a single header, to be used with apr_table_do() */
++static int check_header(struct check_header_ctx *ctx,
++ const char *name, const char **val)
++{
++ const char *pos, *end;
++ char *dst = NULL;
++
++ if (name[0] == '\0') {
++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, APLOGNO(02428)
++ "Empty response header name, aborting request");
++ return 0;
++ }
++
++ if (ctx->strict) {
++ end = ap_scan_http_token(name);
++ }
++ else {
++ end = ap_scan_vchar_obstext(name);
++ }
++ if (*end) {
++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, APLOGNO(02429)
++ "Response header name '%s' contains invalid "
++ "characters, aborting request",
++ name);
++ return 0;
++ }
++
++ for (pos = *val; *pos; pos = end) {
++ end = ap_scan_http_field_content(pos);
++ if (*end) {
++ if (end[0] != CR || end[1] != LF || (end[2] != ' ' &&
++ end[2] != '\t')) {
++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, APLOGNO(02430)
++ "Response header '%s' value of '%s' contains "
++ "invalid characters, aborting request",
++ name, pos);
++ return 0;
++ }
++ if (!dst) {
++ *val = dst = apr_palloc(ctx->r->pool, strlen(*val) + 1);
++ }
++ }
++ if (dst) {
++ memcpy(dst, pos, end - pos);
++ dst += end - pos;
++ if (*end) {
++ /* skip folding and replace with a single space */
++ end += 3 + strspn(end + 3, "\t ");
++ *dst++ = ' ';
++ }
++ }
++ }
++ if (dst) {
++ *dst = '\0';
++ }
++ return 1;
++}
++
++static int check_headers_table(apr_table_t *t, struct check_header_ctx *ctx)
++{
++ const apr_array_header_t *headers = apr_table_elts(t);
++ apr_table_entry_t *header;
++ int i;
++
++ for (i = 0; i < headers->nelts; ++i) {
++ header = &APR_ARRAY_IDX(headers, i, apr_table_entry_t);
++ if (!header->key) {
++ continue;
++ }
++ if (!check_header(ctx, header->key, (const char **)&header->val)) {
++ return 0;
++ }
++ }
++ return 1;
++}
++
++/**
++ * Check headers for HTTP conformance
++ * @return 1 if ok, 0 if bad
++ */
++static APR_INLINE int check_headers(request_rec *r)
++{
++ struct check_header_ctx ctx;
++ core_server_config *conf =
++ ap_get_core_module_config(r->server->module_config);
++
++ ctx.r = r;
++ ctx.strict = (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE);
++ return check_headers_table(r->headers_out, &ctx) &&
++ check_headers_table(r->err_headers_out, &ctx);
++}
++
++static int check_headers_recursion(request_rec *r)
++{
++ void *check = NULL;
++ apr_pool_userdata_get(&check, "check_headers_recursion", r->pool);
++ if (check) {
++ return 1;
++ }
++ apr_pool_userdata_setn("true", "check_headers_recursion", NULL, r->pool);
++ return 0;
++}
++
+ typedef struct header_struct {
+ apr_pool_t *pool;
+ apr_bucket_brigade *bb;
+ } header_struct;
+
+ /* Send a single HTTP header field to the client. Note that this function
+- * is used in calls to table_do(), so their interfaces are co-dependent.
+- * In other words, don't change this one without checking table_do in alloc.c.
++ * is used in calls to apr_table_do(), so don't change its interface.
+ * It returns true unless there was a write error of some kind.
+ */
+ static int form_header_field(header_struct *h,
+@@ -1160,6 +1283,7 @@
+
+ typedef struct header_filter_ctx {
+ int headers_sent;
++ int headers_error;
+ } header_filter_ctx;
+
+ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f,
+@@ -1175,19 +1299,23 @@
+ header_filter_ctx *ctx = f->ctx;
+ const char *ctype;
+ ap_bucket_error *eb = NULL;
++ apr_bucket *eos = NULL;
+
+ AP_DEBUG_ASSERT(!r->main);
+
+- if (r->header_only) {
+- if (!ctx) {
+- ctx = f->ctx = apr_pcalloc(r->pool, sizeof(header_filter_ctx));
+- }
+- else if (ctx->headers_sent) {
++ if (!ctx) {
++ ctx = f->ctx = apr_pcalloc(r->pool, sizeof(header_filter_ctx));
++ }
++ if (ctx->headers_sent) {
++ /* Eat body if response must not have one. */
++ if (r->header_only || r->status == HTTP_NO_CONTENT) {
+ apr_brigade_cleanup(b);
+- return OK;
++ return APR_SUCCESS;
+ }
+ }
+-
++ else if (!ctx->headers_error && !check_headers(r)) {
++ ctx->headers_error = 1;
++ }
+ for (e = APR_BRIGADE_FIRST(b);
+ e != APR_BRIGADE_SENTINEL(b);
+ e = APR_BUCKET_NEXT(e))
+@@ -1204,10 +1332,44 @@
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, b);
+ }
++ if (ctx->headers_error && APR_BUCKET_IS_EOS(e)) {
++ eos = e;
++ }
+ }
+- if (eb) {
+- int status;
++ if (ctx->headers_error) {
++ if (!eos) {
++ /* Eat body until EOS */
++ apr_brigade_cleanup(b);
++ return APR_SUCCESS;
++ }
+
++ /* We may come back here from ap_die() below,
++ * so clear anything from this response.
++ */
++ ctx->headers_error = 0;
++ apr_table_clear(r->headers_out);
++ apr_table_clear(r->err_headers_out);
++
++ /* Don't recall ap_die() if we come back here (from its own internal
++ * redirect or error response), otherwise we can end up in infinite
++ * recursion; better fall through with 500, minimal headers and an
++ * empty body (EOS only).
++ */
++ if (!check_headers_recursion(r)) {
++ apr_brigade_cleanup(b);
++ ap_die(HTTP_INTERNAL_SERVER_ERROR, r);
++ return AP_FILTER_ERROR;
++ }
++ APR_BUCKET_REMOVE(eos);
++ apr_brigade_cleanup(b);
++ APR_BRIGADE_INSERT_TAIL(b, eos);
++ r->status = HTTP_INTERNAL_SERVER_ERROR;
++ r->content_type = r->content_encoding = NULL;
++ r->content_languages = NULL;
++ ap_set_content_length(r, 0);
++ }
++ else if (eb) {
++ int status;
+ status = eb->status;
+ apr_brigade_cleanup(b);
+ ap_die(status, r);
+@@ -1264,6 +1426,10 @@
+ apr_table_unset(r->headers_out, "Content-Length");
+ }
+
++ if (r->status == HTTP_NO_CONTENT) {
++ apr_table_unset(r->headers_out, "Content-Length");
++ }
++
+ ctype = ap_make_content_type(r, r->content_type);
+ if (ctype) {
+ apr_table_setn(r->headers_out, "Content-Type", ctype);
+@@ -1352,11 +1518,11 @@
+ terminate_header(b2);
+
+ ap_pass_brigade(f->next, b2);
++ ctx->headers_sent = 1;
+
+- if (r->header_only) {
++ if (r->header_only || r->status == HTTP_NO_CONTENT) {
+ apr_brigade_cleanup(b);
+- ctx->headers_sent = 1;
+- return OK;
++ return APR_SUCCESS;
+ }
+
+ r->sent_bodyct = 1; /* Whatever follows is real body stuff... */
+diff -uap httpd-2.4.6/server/core.c.cve8743 httpd-2.4.6/server/core.c
+--- httpd-2.4.6/server/core.c.cve8743
++++ httpd-2.4.6/server/core.c
+@@ -506,6 +506,15 @@
+ if (virt->trace_enable != AP_TRACE_UNSET)
+ conf->trace_enable = virt->trace_enable;
+
++ if (virt->http09_enable != AP_HTTP09_UNSET)
++ conf->http09_enable = virt->http09_enable;
++
++ if (virt->http_conformance != AP_HTTP_CONFORMANCE_UNSET)
++ conf->http_conformance = virt->http_conformance;
++
++ if (virt->http_methods != AP_HTTP_METHODS_UNSET)
++ conf->http_methods = virt->http_methods;
++
+ /* no action for virt->accf_map, not allowed per-vhost */
+
+ if (virt->protocol)
+@@ -3632,6 +3641,57 @@
+ return NULL;
+ }
+
++static const char *set_http_protocol_options(cmd_parms *cmd, void *dummy,
++ const char *arg)
++{
++ core_server_config *conf =
++ ap_get_core_module_config(cmd->server->module_config);
++
++ if (strcasecmp(arg, "allow0.9") == 0)
++ conf->http09_enable |= AP_HTTP09_ENABLE;
++ else if (strcasecmp(arg, "require1.0") == 0)
++ conf->http09_enable |= AP_HTTP09_DISABLE;
++ else if (strcasecmp(arg, "strict") == 0)
++ conf->http_conformance |= AP_HTTP_CONFORMANCE_STRICT;
++ else if (strcasecmp(arg, "unsafe") == 0)
++ conf->http_conformance |= AP_HTTP_CONFORMANCE_UNSAFE;
++ else if (strcasecmp(arg, "registeredmethods") == 0)
++ conf->http_methods |= AP_HTTP_METHODS_REGISTERED;
++ else if (strcasecmp(arg, "lenientmethods") == 0)
++ conf->http_methods |= AP_HTTP_METHODS_LENIENT;
++ else
++ return "HttpProtocolOptions accepts "
++ "'Unsafe' or 'Strict' (default), "
++ "'RegisteredMethods' or 'LenientMethods' (default), and "
++ "'Require1.0' or 'Allow0.9' (default)";
++
++ if ((conf->http09_enable & AP_HTTP09_ENABLE)
++ && (conf->http09_enable & AP_HTTP09_DISABLE))
++ return "HttpProtocolOptions 'Allow0.9' and 'Require1.0'"
++ " are mutually exclusive";
++
++ if ((conf->http_conformance & AP_HTTP_CONFORMANCE_STRICT)
++ && (conf->http_conformance & AP_HTTP_CONFORMANCE_UNSAFE))
++ return "HttpProtocolOptions 'Strict' and 'Unsafe'"
++ " are mutually exclusive";
++
++ if ((conf->http_methods & AP_HTTP_METHODS_REGISTERED)
++ && (conf->http_methods & AP_HTTP_METHODS_LENIENT))
++ return "HttpProtocolOptions 'RegisteredMethods' and 'LenientMethods'"
++ " are mutually exclusive";
++
++ return NULL;
++}
++
++static const char *set_http_method(cmd_parms *cmd, void *conf, const char *arg)
++{
++ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
++ if (err != NULL)
++ return err;
++ ap_method_register(cmd->pool, arg);
++ return NULL;
++}
++
+ static apr_hash_t *errorlog_hash;
+
+ static int log_constant_item(const ap_errorlog_info *info, const char *arg,
+@@ -4143,6 +4203,13 @@
+ "'on' (default), 'off' or 'extended' to trace request body content"),
+ AP_INIT_FLAG("MergeTrailers", set_merge_trailers, NULL, RSRC_CONF,
+ "merge request trailers into request headers or not"),
++AP_INIT_ITERATE("HttpProtocolOptions", set_http_protocol_options, NULL, RSRC_CONF,
++ "'Allow0.9' or 'Require1.0' (default); "
++ "'RegisteredMethods' or 'LenientMethods' (default); "
++ "'Unsafe' or 'Strict' (default). Sets HTTP acceptance rules")
++,
++AP_INIT_ITERATE("RegisterHttpMethod", set_http_method, NULL, RSRC_CONF,
++ "Registers non-standard HTTP methods"),
+ { NULL }
+ };
+
+diff -uap httpd-2.4.6/server/gen_test_char.c.cve8743 httpd-2.4.6/server/gen_test_char.c
+--- httpd-2.4.6/server/gen_test_char.c.cve8743
++++ httpd-2.4.6/server/gen_test_char.c
+@@ -16,11 +16,11 @@
+
+ #ifdef CROSS_COMPILE
+
++#include
+ #define apr_isalnum(c) (isalnum(((unsigned char)(c))))
+ #define apr_isalpha(c) (isalpha(((unsigned char)(c))))
+ #define apr_iscntrl(c) (iscntrl(((unsigned char)(c))))
+ #define apr_isprint(c) (isprint(((unsigned char)(c))))
+-#include
+ #define APR_HAVE_STDIO_H 1
+ #define APR_HAVE_STRING_H 1
+
+@@ -52,11 +52,13 @@
+ #define T_ESCAPE_LOGITEM (0x10)
+ #define T_ESCAPE_FORENSIC (0x20)
+ #define T_ESCAPE_URLENCODED (0x40)
++#define T_HTTP_CTRLS (0x80)
++#define T_VCHAR_OBSTEXT (0x100)
+
+ int main(int argc, char *argv[])
+ {
+ unsigned c;
+- unsigned char flags;
++ unsigned short flags;
+
+ printf("/* this file is automatically generated by gen_test_char, "
+ "do not edit */\n"
+@@ -67,19 +69,23 @@
+ "#define T_ESCAPE_LOGITEM (%u)\n"
+ "#define T_ESCAPE_FORENSIC (%u)\n"
+ "#define T_ESCAPE_URLENCODED (%u)\n"
++ "#define T_HTTP_CTRLS (%u)\n"
++ "#define T_VCHAR_OBSTEXT (%u)\n"
+ "\n"
+- "static const unsigned char test_char_table[256] = {",
++ "static const unsigned short test_char_table[256] = {",
+ T_ESCAPE_SHELL_CMD,
+ T_ESCAPE_PATH_SEGMENT,
+ T_OS_ESCAPE_PATH,
+ T_HTTP_TOKEN_STOP,
+ T_ESCAPE_LOGITEM,
+ T_ESCAPE_FORENSIC,
+- T_ESCAPE_URLENCODED);
++ T_ESCAPE_URLENCODED,
++ T_HTTP_CTRLS,
++ T_VCHAR_OBSTEXT);
+
+ for (c = 0; c < 256; ++c) {
+ flags = 0;
+- if (c % 20 == 0)
++ if (c % 8 == 0)
+ printf("\n ");
+
+ /* escape_shell_cmd */
+@@ -107,7 +113,7 @@
+ flags |= T_ESCAPE_PATH_SEGMENT;
+ }
+
+- if (!apr_isalnum(c) && !strchr("$-_.+!*'(),:@&=/~", c)) {
++ if (!apr_isalnum(c) && !strchr("$-_.+!*'(),:;@&=/~", c)) {
+ flags |= T_OS_ESCAPE_PATH;
+ }
+
+@@ -115,11 +121,32 @@
+ flags |= T_ESCAPE_URLENCODED;
+ }
+
+- /* these are the "tspecials" (RFC2068) or "separators" (RFC2616) */
+- if (c && (apr_iscntrl(c) || strchr(" \t()<>@,;:\\\"/[]?={}", c))) {
++ /* Stop for any non-'token' character, including ctrls, obs-text,
++ * and "tspecials" (RFC2068) a.k.a. "separators" (RFC2616), which
++ * is easer to express as characters remaining in the ASCII token set
++ */
++ if (!c || !(apr_isalnum(c) || strchr("!#$%&'*+-.^_`|~", c))) {
+ flags |= T_HTTP_TOKEN_STOP;
+ }
+
++ /* Catch CTRLs other than VCHAR, HT and SP, and obs-text (RFC7230 3.2)
++ * This includes only the C0 plane, not C1 (which is obs-text itself.)
++ * XXX: We should verify that all ASCII C0 ctrls/DEL corresponding to
++ * the current EBCDIC translation are captured, and ASCII C1 ctrls
++ * corresponding are all permitted (as they fall under obs-text rule)
++ */
++ if (!c || (apr_iscntrl(c) && c != '\t')) {
++ flags |= T_HTTP_CTRLS;
++ }
++
++ /* From RFC3986, the specific sets of gen-delims, sub-delims (2.2),
++ * and unreserved (2.3) that are possible somewhere within a URI.
++ * Spec requires all others to be %XX encoded, including obs-text.
++ */
++ if (c && !apr_iscntrl(c) && c != ' ') {
++ flags |= T_VCHAR_OBSTEXT;
++ }
++
+ /* For logging, escape all control characters,
+ * double quotes (because they delimit the request in the log file)
+ * backslashes (because we use backslash for escaping)
+@@ -137,7 +164,7 @@
+ flags |= T_ESCAPE_FORENSIC;
+ }
+
+- printf("%u%c", flags, (c < 255) ? ',' : ' ');
++ printf("0x%03x%c", flags, (c < 255) ? ',' : ' ');
+ }
+
+ printf("\n};\n");
+diff -uap httpd-2.4.6/server/protocol.c.cve8743 httpd-2.4.6/server/protocol.c
+--- httpd-2.4.6/server/protocol.c.cve8743
++++ httpd-2.4.6/server/protocol.c
+@@ -189,6 +189,10 @@
+ * caused by MIME folding (or broken clients) if fold != 0, and place it
+ * in the buffer s, of size n bytes, without the ending newline.
+ *
++ * Pulls from r->proto_input_filters instead of r->input_filters for
++ * stricter protocol adherence and better input filter behavior during
++ * chunked trailer processing (for http).
++ *
+ * If s is NULL, ap_rgetline_core will allocate necessary memory from r->pool.
+ *
+ * Returns APR_SUCCESS if there are no problems and sets *read to be
+@@ -197,7 +201,7 @@
+ * APR_ENOSPC is returned if there is not enough buffer space.
+ * Other errors may be returned on other errors.
+ *
+- * The LF is *not* returned in the buffer. Therefore, a *read of 0
++ * The [CR]LF are *not* returned in the buffer. Therefore, a *read of 0
+ * indicates that an empty line was read.
+ *
+ * Notes: Because the buffer uses 1 char for NUL, the most we can return is
+@@ -208,13 +212,15 @@
+ */
+ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
+ apr_size_t *read, request_rec *r,
+- int fold, apr_bucket_brigade *bb)
++ int flags, apr_bucket_brigade *bb)
+ {
+ apr_status_t rv;
+ apr_bucket *e;
+ apr_size_t bytes_handled = 0, current_alloc = 0;
+ char *pos, *last_char = *s;
+ int do_alloc = (*s == NULL), saw_eos = 0;
++ int fold = flags & AP_GETLINE_FOLD;
++ int crlf = flags & AP_GETLINE_CRLF;
+
+ /*
+ * Initialize last_char as otherwise a random value will be compared
+@@ -226,13 +232,15 @@
+
+ for (;;) {
+ apr_brigade_cleanup(bb);
+- rv = ap_get_brigade(r->input_filters, bb, AP_MODE_GETLINE,
++ rv = ap_get_brigade(r->proto_input_filters, bb, AP_MODE_GETLINE,
+ APR_BLOCK_READ, 0);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+
+- /* Something horribly wrong happened. Someone didn't block! */
++ /* Something horribly wrong happened. Someone didn't block!
++ * (this also happens at the end of each keepalive connection)
++ */
+ if (APR_BRIGADE_EMPTY(bb)) {
+ return APR_EGENERAL;
+ }
+@@ -318,6 +326,13 @@
+ }
+ }
+
++ if (crlf && (last_char <= *s || last_char[-1] != APR_ASCII_CR)) {
++ *last_char = '\0';
++ bytes_handled = last_char - *s;
++ *read = bytes_handled;
++ return APR_EINVAL;
++ }
++
+ /* Now NUL-terminate the string at the end of the line;
+ * if the last-but-one character is a CR, terminate there */
+ if (last_char > *s && last_char[-1] == APR_ASCII_CR) {
+@@ -340,7 +355,7 @@
+ apr_brigade_cleanup(bb);
+
+ /* We only care about the first byte. */
+- rv = ap_get_brigade(r->input_filters, bb, AP_MODE_SPECULATIVE,
++ rv = ap_get_brigade(r->proto_input_filters, bb, AP_MODE_SPECULATIVE,
+ APR_BLOCK_READ, 1);
+ if (rv != APR_SUCCESS) {
+ return rv;
+@@ -391,7 +406,8 @@
+ */
+ if (do_alloc) {
+ tmp = NULL;
+- } else {
++ }
++ else {
+ /* We're null terminated. */
+ tmp = last_char;
+ }
+@@ -461,7 +477,7 @@
+ }
+ #endif
+
+-AP_DECLARE(int) ap_getline(char *s, int n, request_rec *r, int fold)
++AP_DECLARE(int) ap_getline(char *s, int n, request_rec *r, int flags)
+ {
+ char *tmp_s = s;
+ apr_status_t rv;
+@@ -469,7 +485,7 @@
+ apr_bucket_brigade *tmp_bb;
+
+ tmp_bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+- rv = ap_rgetline(&tmp_s, n, &len, r, fold, tmp_bb);
++ rv = ap_rgetline(&tmp_s, n, &len, r, flags, tmp_bb);
+ apr_brigade_destroy(tmp_bb);
+
+ /* Map the out-of-space condition to the old API. */
+@@ -549,16 +565,29 @@
+ }
+ }
+
+-static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
++/* get the length of the field name for logging, but no more than 80 bytes */
++#define LOG_NAME_MAX_LEN 80
++static int field_name_len(const char *field)
+ {
+- const char *ll;
+- const char *uri;
+- const char *pro;
++ const char *end = ap_strchr_c(field, ':');
++ if (end == NULL || end - field > LOG_NAME_MAX_LEN)
++ return LOG_NAME_MAX_LEN;
++ return end - field;
++}
+
+- int major = 1, minor = 0; /* Assume HTTP/1.0 if non-"HTTP" protocol */
+- char http[5];
++static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
++{
++ enum {
++ rrl_none, rrl_badmethod, rrl_badwhitespace, rrl_excesswhitespace,
++ rrl_missinguri, rrl_baduri, rrl_badprotocol, rrl_trailingtext,
++ rrl_badmethod09, rrl_reject09
++ } deferred_error = rrl_none;
++ char *ll;
++ char *uri;
+ apr_size_t len;
+ int num_blank_lines = 0;
++ core_server_config *conf = ap_get_core_module_config(r->server->module_config);
++ int strict = (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE);
+ int max_blank_lines = r->server->limit_req_fields;
+
+ if (max_blank_lines <= 0) {
+@@ -588,7 +617,7 @@
+ */
+ r->the_request = NULL;
+ rv = ap_rgetline(&(r->the_request), (apr_size_t)(r->server->limit_req_line + 2),
+- &len, r, 0, bb);
++ &len, r, strict ? AP_GETLINE_CRLF : 0, bb);
+
+ if (rv != APR_SUCCESS) {
+ r->request_time = apr_time_now();
+@@ -599,8 +628,6 @@
+ */
+ if (APR_STATUS_IS_ENOSPC(rv)) {
+ r->status = HTTP_REQUEST_URI_TOO_LARGE;
+- r->proto_num = HTTP_VERSION(1,0);
+- r->protocol = apr_pstrdup(r->pool, "HTTP/1.0");
+ }
+ else if (APR_STATUS_IS_TIMEUP(rv)) {
+ r->status = HTTP_REQUEST_TIME_OUT;
+@@ -608,6 +635,8 @@
+ else if (APR_STATUS_IS_EINVAL(rv)) {
+ r->status = HTTP_BAD_REQUEST;
+ }
++ r->proto_num = HTTP_VERSION(1,0);
++ r->protocol = apr_pstrdup(r->pool, "HTTP/1.0");
+ return 0;
+ }
+ } while ((len <= 0) && (++num_blank_lines < max_blank_lines));
+@@ -619,46 +648,263 @@
+ }
+
+ r->request_time = apr_time_now();
+- ll = r->the_request;
+- r->method = ap_getword_white(r->pool, &ll);
++ r->method = r->the_request;
++
++ /* If there is whitespace before a method, skip it and mark in error */
++ if (apr_isspace(*r->method)) {
++ deferred_error = rrl_badwhitespace;
++ for ( ; apr_isspace(*r->method); ++r->method)
++ ;
++ }
+
+- uri = ap_getword_white(r->pool, &ll);
++ /* Scan the method up to the next whitespace, ensure it contains only
++ * valid http-token characters, otherwise mark in error
++ */
++ if (strict) {
++ ll = (char*) ap_scan_http_token(r->method);
++ }
++ else {
++ ll = (char*) ap_scan_vchar_obstext(r->method);
++ }
+
+- /* Provide quick information about the request method as soon as known */
++ if (((ll == r->method) || (*ll && !apr_isspace(*ll)))
++ && deferred_error == rrl_none) {
++ deferred_error = rrl_badmethod;
++ ll = strpbrk(ll, "\t\n\v\f\r ");
++ }
+
+- r->method_number = ap_method_number_of(r->method);
+- if (r->method_number == M_GET && r->method[0] == 'H') {
+- r->header_only = 1;
++ /* Verify method terminated with a single SP, or mark as specific error */
++ if (!ll) {
++ if (deferred_error == rrl_none)
++ deferred_error = rrl_missinguri;
++ r->protocol = uri = "";
++ len = 0;
++ goto rrl_done;
++ }
++ else if (strict && ll[0] && apr_isspace(ll[1])
++ && deferred_error == rrl_none) {
++ deferred_error = rrl_excesswhitespace;
+ }
+
+- ap_parse_uri(r, uri);
++ /* Advance uri pointer over leading whitespace, NUL terminate the method
++ * If non-SP whitespace is encountered, mark as specific error
++ */
++ for (uri = ll; apr_isspace(*uri); ++uri)
++ if (*uri != ' ' && deferred_error == rrl_none)
++ deferred_error = rrl_badwhitespace;
++ *ll = '\0';
++
++ if (!*uri && deferred_error == rrl_none)
++ deferred_error = rrl_missinguri;
++
++ /* Scan the URI up to the next whitespace, ensure it contains no raw
++ * control characters, otherwise mark in error
++ */
++ ll = (char*) ap_scan_vchar_obstext(uri);
++ if (ll == uri || (*ll && !apr_isspace(*ll))) {
++ deferred_error = rrl_baduri;
++ ll = strpbrk(ll, "\t\n\v\f\r ");
++ }
+
+- if (ll[0]) {
++ /* Verify URI terminated with a single SP, or mark as specific error */
++ if (!ll) {
++ r->protocol = "";
++ len = 0;
++ goto rrl_done;
++ }
++ else if (strict && ll[0] && apr_isspace(ll[1])
++ && deferred_error == rrl_none) {
++ deferred_error = rrl_excesswhitespace;
++ }
++
++ /* Advance protocol pointer over leading whitespace, NUL terminate the uri
++ * If non-SP whitespace is encountered, mark as specific error
++ */
++ for (r->protocol = ll; apr_isspace(*r->protocol); ++r->protocol)
++ if (*r->protocol != ' ' && deferred_error == rrl_none)
++ deferred_error = rrl_badwhitespace;
++ *ll = '\0';
++
++ /* Scan the protocol up to the next whitespace, validation comes later */
++ if (!(ll = (char*) ap_scan_vchar_obstext(r->protocol))) {
++ len = strlen(r->protocol);
++ goto rrl_done;
++ }
++ len = ll - r->protocol;
++
++ /* Advance over trailing whitespace, if found mark in error,
++ * determine if trailing text is found, unconditionally mark in error,
++ * finally NUL terminate the protocol string
++ */
++ if (*ll && !apr_isspace(*ll)) {
++ deferred_error = rrl_badprotocol;
++ }
++ else if (strict && *ll) {
++ deferred_error = rrl_excesswhitespace;
++ }
++ else {
++ for ( ; apr_isspace(*ll); ++ll)
++ if (*ll != ' ' && deferred_error == rrl_none)
++ deferred_error = rrl_badwhitespace;
++ if (*ll && deferred_error == rrl_none)
++ deferred_error = rrl_trailingtext;
++ }
++ *((char *)r->protocol + len) = '\0';
++
++rrl_done:
++ /* For internal integrety and palloc efficiency, reconstruct the_request
++ * in one palloc, using only single SP characters, per spec.
++ */
++ r->the_request = apr_pstrcat(r->pool, r->method, *uri ? " " : NULL, uri,
++ *r->protocol ? " " : NULL, r->protocol, NULL);
++
++ if (len == 8
++ && r->protocol[0] == 'H' && r->protocol[1] == 'T'
++ && r->protocol[2] == 'T' && r->protocol[3] == 'P'
++ && r->protocol[4] == '/' && apr_isdigit(r->protocol[5])
++ && r->protocol[6] == '.' && apr_isdigit(r->protocol[7])
++ && r->protocol[5] != '0') {
++ r->assbackwards = 0;
++ r->proto_num = HTTP_VERSION(r->protocol[5] - '0', r->protocol[7] - '0');
++ }
++ else if (len == 8
++ && (r->protocol[0] == 'H' || r->protocol[0] == 'h')
++ && (r->protocol[1] == 'T' || r->protocol[1] == 't')
++ && (r->protocol[2] == 'T' || r->protocol[2] == 't')
++ && (r->protocol[3] == 'P' || r->protocol[3] == 'p')
++ && r->protocol[4] == '/' && apr_isdigit(r->protocol[5])
++ && r->protocol[6] == '.' && apr_isdigit(r->protocol[7])
++ && r->protocol[5] != '0') {
+ r->assbackwards = 0;
+- pro = ll;
+- len = strlen(ll);
+- } else {
++ r->proto_num = HTTP_VERSION(r->protocol[5] - '0', r->protocol[7] - '0');
++ if (strict && deferred_error == rrl_none)
++ deferred_error = rrl_badprotocol;
++ else
++ memcpy((char*)r->protocol, "HTTP", 4);
++ } else if (r->protocol[0]) {
++ r->proto_num = HTTP_VERSION(0, 9);
++ /* Defer setting the r->protocol string till error msg is composed */
++ if (deferred_error == rrl_none)
++ deferred_error = rrl_badprotocol;
++ }
++ else {
+ r->assbackwards = 1;
+- pro = "HTTP/0.9";
+- len = 8;
++ r->protocol = apr_pstrdup(r->pool, "HTTP/0.9");
++ r->proto_num = HTTP_VERSION(0, 9);
+ }
+- r->protocol = apr_pstrmemdup(r->pool, pro, len);
+
+- /* Avoid sscanf in the common case */
+- if (len == 8
+- && pro[0] == 'H' && pro[1] == 'T' && pro[2] == 'T' && pro[3] == 'P'
+- && pro[4] == '/' && apr_isdigit(pro[5]) && pro[6] == '.'
+- && apr_isdigit(pro[7])) {
+- r->proto_num = HTTP_VERSION(pro[5] - '0', pro[7] - '0');
+- }
+- else if (3 == sscanf(r->protocol, "%4s/%u.%u", http, &major, &minor)
+- && (strcasecmp("http", http) == 0)
+- && (minor < HTTP_VERSION(1, 0)) ) /* don't allow HTTP/0.1000 */
+- r->proto_num = HTTP_VERSION(major, minor);
+- else
+- r->proto_num = HTTP_VERSION(1, 0);
++ /* Determine the method_number and parse the uri prior to invoking error
++ * handling, such that these fields are available for subsitution
++ */
++ r->method_number = ap_method_number_of(r->method);
++ if (r->method_number == M_GET && r->method[0] == 'H')
++ r->header_only = 1;
++
++ ap_parse_uri(r, uri);
++
++ /* With the request understood, we can consider HTTP/0.9 specific errors */
++ if (r->proto_num == HTTP_VERSION(0, 9) && deferred_error == rrl_none) {
++ if (conf->http09_enable == AP_HTTP09_DISABLE)
++ deferred_error = rrl_reject09;
++ else if (strict && (r->method_number != M_GET || r->header_only))
++ deferred_error = rrl_badmethod09;
++ }
++
++ /* Now that the method, uri and protocol are all processed,
++ * we can safely resume any deferred error reporting
++ */
++ if (deferred_error != rrl_none) {
++ if (deferred_error == rrl_badmethod)
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03445)
++ "HTTP Request Line; Invalid method token: '%.*s'",
++ field_name_len(r->method), r->method);
++ else if (deferred_error == rrl_badmethod09)
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03444)
++ "HTTP Request Line; Invalid method token: '%.*s'"
++ " (only GET is allowed for HTTP/0.9 requests)",
++ field_name_len(r->method), r->method);
++ else if (deferred_error == rrl_missinguri)
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03446)
++ "HTTP Request Line; Missing URI");
++ else if (deferred_error == rrl_baduri)
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03454)
++ "HTTP Request Line; URI incorrectly encoded: '%.*s'",
++ field_name_len(r->uri), r->uri);
++ else if (deferred_error == rrl_badwhitespace)
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03447)
++ "HTTP Request Line; Invalid whitespace");
++ else if (deferred_error == rrl_excesswhitespace)
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03448)
++ "HTTP Request Line; Excess whitespace "
++ "(disallowed by HttpProtocolOptions Strict");
++ else if (deferred_error == rrl_trailingtext)
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03449)
++ "HTTP Request Line; Extraneous text found '%.*s' "
++ "(perhaps whitespace was injected?)",
++ field_name_len(ll), ll);
++ else if (deferred_error == rrl_reject09)
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02401)
++ "HTTP Request Line; Rejected HTTP/0.9 request");
++ else if (deferred_error == rrl_badprotocol)
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02418)
++ "HTTP Request Line; Unrecognized protocol '%.*s' "
++ "(perhaps whitespace was injected?)",
++ field_name_len(r->protocol), r->protocol);
++ r->status = HTTP_BAD_REQUEST;
++ goto rrl_failed;
++ }
++
++ if (conf->http_methods == AP_HTTP_METHODS_REGISTERED
++ && r->method_number == M_INVALID) {
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02423)
++ "HTTP Request Line; Unrecognized HTTP method: '%.*s' "
++ "(disallowed by RegisteredMethods)",
++ field_name_len(r->method), r->method);
++ r->status = HTTP_NOT_IMPLEMENTED;
++ /* This can't happen in an HTTP/0.9 request, we verified GET above */
++ return 0;
++ }
++
++ if (r->status != HTTP_OK) {
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03450)
++ "HTTP Request Line; Unable to parse URI: '%.*s'",
++ field_name_len(r->uri), r->uri);
++ goto rrl_failed;
++ }
++
++ if (strict) {
++ if (r->parsed_uri.fragment) {
++ /* RFC3986 3.5: no fragment */
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02421)
++ "HTTP Request Line; URI must not contain a fragment");
++ r->status = HTTP_BAD_REQUEST;
++ goto rrl_failed;
++ }
++ if (r->parsed_uri.user || r->parsed_uri.password) {
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02422)
++ "HTTP Request Line; URI must not contain a "
++ "username/password");
++ r->status = HTTP_BAD_REQUEST;
++ goto rrl_failed;
++ }
++ }
+
+ return 1;
++rrl_failed:
++ if (r->proto_num == HTTP_VERSION(0, 9)) {
++ /* Send all parsing and protocol error response with 1.x behavior,
++ * and reserve 505 errors for actual HTTP protocols presented.
++ * As called out in RFC7230 3.5, any errors parsing the protocol
++ * from the request line are nearly always misencoded HTTP/1.x
++ * requests. Only a valid 0.9 request with no parsing errors
++ * at all may be treated as a simple request, if allowed.
++ */
++ r->assbackwards = 0;
++ r->connection->keepalive = AP_CONN_CLOSE;
++ r->proto_num = HTTP_VERSION(1, 0);
++ r->protocol = apr_pstrdup(r->pool, "HTTP/1.0");
++ }
++ return 0;
+ }
+
+ static int table_do_fn_check_lengths(void *r_, const char *key,
+@@ -670,26 +916,13 @@
+
+ r->status = HTTP_BAD_REQUEST;
+ apr_table_setn(r->notes, "error-notes",
+- apr_pstrcat(r->pool, "Size of a request header field "
+- "after merging exceeds server limit. "
+- "\n\n",
+- ap_escape_html(r->pool, key),
+- " \n", NULL));
+- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00560) "Request header "
+- "exceeds LimitRequestFieldSize after merging: %s", key);
++ "Size of a request header field exceeds server limit.");
++ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00560) "Request "
++ "header exceeds LimitRequestFieldSize after merging: %.*s",
++ field_name_len(key), key);
+ return 0;
+ }
+
+-/* get the length of the field name for logging, but no more than 80 bytes */
+-#define LOG_NAME_MAX_LEN 80
+-static int field_name_len(const char *field)
+-{
+- const char *end = ap_strchr_c(field, ':');
+- if (end == NULL || end - field > LOG_NAME_MAX_LEN)
+- return LOG_NAME_MAX_LEN;
+- return end - field;
+-}
+-
+ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb)
+ {
+ char *last_field = NULL;
+@@ -700,6 +933,8 @@
+ apr_size_t len;
+ int fields_read = 0;
+ char *tmp_field;
++ core_server_config *conf = ap_get_core_module_config(r->server->module_config);
++ int strict = (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE);
+
+ /*
+ * Read header lines until we get the empty separator line, a read error,
+@@ -707,11 +942,10 @@
+ */
+ while(1) {
+ apr_status_t rv;
+- int folded = 0;
+
+ field = NULL;
+ rv = ap_rgetline(&field, r->server->limit_req_fieldsize + 2,
+- &len, r, 0, bb);
++ &len, r, strict ? AP_GETLINE_CRLF : 0, bb);
+
+ if (rv != APR_SUCCESS) {
+ if (APR_STATUS_IS_TIMEUP(rv)) {
+@@ -728,153 +962,217 @@
+ * exceeds the configured limit for a field size.
+ */
+ if (rv == APR_ENOSPC) {
+- const char *field_escaped;
+- if (field) {
+- /* ensure ap_escape_html will terminate correctly */
+- field[len - 1] = '\0';
+- field_escaped = ap_escape_html(r->pool, field);
+- }
+- else {
+- field_escaped = field = "";
+- }
+-
+ apr_table_setn(r->notes, "error-notes",
+- apr_psprintf(r->pool,
+- "Size of a request header field "
+- "exceeds server limit. \n"
+- "\n%.*s\n \n",
+- field_name_len(field_escaped),
+- field_escaped));
++ "Size of a request header field "
++ "exceeds server limit.");
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00561)
+ "Request header exceeds LimitRequestFieldSize%s"
+ "%.*s",
+- *field ? ": " : "",
+- field_name_len(field), field);
++ (field && *field) ? ": " : "",
++ (field) ? field_name_len(field) : 0,
++ (field) ? field : "");
+ }
+ return;
+ }
+
+- if (last_field != NULL) {
+- if ((len > 0) && ((*field == '\t') || *field == ' ')) {
+- /* This line is a continuation of the preceding line(s),
+- * so append it to the line that we've set aside.
+- * Note: this uses a power-of-two allocator to avoid
+- * doing O(n) allocs and using O(n^2) space for
+- * continuations that span many many lines.
+- */
+- apr_size_t fold_len = last_len + len + 1; /* trailing null */
+
+- if (fold_len >= (apr_size_t)(r->server->limit_req_fieldsize)) {
+- r->status = HTTP_BAD_REQUEST;
+- /* report what we have accumulated so far before the
+- * overflow (last_field) as the field with the problem
+- */
+- apr_table_setn(r->notes, "error-notes",
+- apr_psprintf(r->pool,
+- "Size of a request header field "
+- "after folding "
+- "exceeds server limit. \n"
+- "\n%.*s\n \n",
+- field_name_len(last_field),
+- ap_escape_html(r->pool, last_field)));
+- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00562)
+- "Request header exceeds LimitRequestFieldSize "
+- "after folding: %.*s",
+- field_name_len(last_field), last_field);
+- return;
+- }
++ /* For all header values, and all obs-fold lines, the presence of
++ * additional whitespace is a no-op, so collapse trailing whitespace
++ * to save buffer allocation and optimize copy operations.
++ * Do not remove the last single whitespace under any condition.
++ */
++ while (len > 1 && (field[len-1] == '\t' || field[len-1] == ' ')) {
++ field[--len] = '\0';
++ }
++
++ if (*field == '\t' || *field == ' ') {
++ /* Append any newly-read obs-fold line onto the preceding
++ * last_field line we are processing
++ */
++ apr_size_t fold_len;
+
++ if (last_field == NULL) {
++ r->status = HTTP_BAD_REQUEST;
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03442)
++ "Line folding encountered before first"
++ " header line");
++ return;
++ }
++
++ if (field[1] == '\0') {
++ r->status = HTTP_BAD_REQUEST;
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03443)
++ "Empty folded line encountered");
++ return;
++ }
++
++ /* Leading whitespace on an obs-fold line can be
++ * similarly discarded */
++ while (field[1] == '\t' || field[1] == ' ') {
++ ++field; --len;
++ }
++
++ /* This line is a continuation of the preceding line(s),
++ * so append it to the line that we've set aside.
++ * Note: this uses a power-of-two allocator to avoid
++ * doing O(n) allocs and using O(n^2) space for
++ * continuations that span many many lines.
++ */
++ fold_len = last_len + len + 1; /* trailing null */
++
++ if (fold_len >= (apr_size_t)(r->server->limit_req_fieldsize)) {
++ r->status = HTTP_BAD_REQUEST;
++ /* report what we have accumulated so far before the
++ * overflow (last_field) as the field with the problem
++ */
++ apr_table_setn(r->notes, "error-notes",
++ "Size of a request header field "
++ "exceeds server limit.");
++ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00562)
++ "Request header exceeds LimitRequestFieldSize "
++ "after folding: %.*s",
++ field_name_len(last_field), last_field);
++ return;
++ }
++
++ if (fold_len > alloc_len) {
++ char *fold_buf;
++ alloc_len += alloc_len;
+ if (fold_len > alloc_len) {
+- char *fold_buf;
+- alloc_len += alloc_len;
+- if (fold_len > alloc_len) {
+- alloc_len = fold_len;
+- }
+- fold_buf = (char *)apr_palloc(r->pool, alloc_len);
+- memcpy(fold_buf, last_field, last_len);
+- last_field = fold_buf;
++ alloc_len = fold_len;
+ }
+- memcpy(last_field + last_len, field, len +1); /* +1 for nul */
+- last_len += len;
+- folded = 1;
+- }
+- else /* not a continuation line */ {
++ fold_buf = (char *)apr_palloc(r->pool, alloc_len);
++ memcpy(fold_buf, last_field, last_len);
++ last_field = fold_buf;
++ }
++ memcpy(last_field + last_len, field, len +1); /* +1 for nul */
++ /* Replace obs-fold w/ SP per RFC 7230 3.2.4 */
++ last_field[last_len] = ' ';
++ last_len += len;
+
+- if (r->server->limit_req_fields
++ /* We've appended this obs-fold line to last_len, proceed to
++ * read the next input line
++ */
++ continue;
++ }
++ else if (last_field != NULL) {
++ /* Process the previous last_field header line with all obs-folded
++ * segments already concatinated (this is not operating on the
++ * most recently read input line).
++ */
++ if (r->server->limit_req_fields
+ && (++fields_read > r->server->limit_req_fields)) {
+- r->status = HTTP_BAD_REQUEST;
+- apr_table_setn(r->notes, "error-notes",
+- "The number of request header fields "
+- "exceeds this server's limit.");
+- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00563)
+- "Number of request headers exceeds "
+- "LimitRequestFields");
+- return;
+- }
++ r->status = HTTP_BAD_REQUEST;
++ apr_table_setn(r->notes, "error-notes",
++ "The number of request header fields "
++ "exceeds this server's limit.");
++ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00563)
++ "Number of request headers exceeds "
++ "LimitRequestFields");
++ return;
++ }
+
+- if (!(value = strchr(last_field, ':'))) { /* Find ':' or */
+- r->status = HTTP_BAD_REQUEST; /* abort bad request */
+- apr_table_setn(r->notes, "error-notes",
+- apr_psprintf(r->pool,
+- "Request header field is "
+- "missing ':' separator. \n"
+- "\n%.*s \n",
+- (int)LOG_NAME_MAX_LEN,
+- ap_escape_html(r->pool,
+- last_field)));
+- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00564)
++ if (!strict)
++ {
++ /* Not Strict ('Unsafe' mode), using the legacy parser */
++
++ if (!(value = strchr(last_field, ':'))) { /* Find ':' or */
++ r->status = HTTP_BAD_REQUEST; /* abort bad request */
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00564)
+ "Request header field is missing ':' "
+ "separator: %.*s", (int)LOG_NAME_MAX_LEN,
+ last_field);
++
+ return;
+ }
+
+- tmp_field = value - 1; /* last character of field-name */
++ /* last character of field-name */
++ tmp_field = value - (value > last_field ? 1 : 0);
+
+ *value++ = '\0'; /* NUL-terminate at colon */
+
++ if (strpbrk(last_field, "\t\n\v\f\r ")) {
++ r->status = HTTP_BAD_REQUEST;
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03452)
++ "Request header field name presented"
++ " invalid whitespace");
++ return;
++ }
++
+ while (*value == ' ' || *value == '\t') {
+- ++value; /* Skip to start of value */
++ ++value; /* Skip to start of value */
++ }
++
++ if (strpbrk(value, "\n\v\f\r")) {
++ r->status = HTTP_BAD_REQUEST;
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03451)
++ "Request header field value presented"
++ " bad whitespace");
++ return;
+ }
+
+- /* Strip LWS after field-name: */
+- while (tmp_field > last_field
+- && (*tmp_field == ' ' || *tmp_field == '\t')) {
+- *tmp_field-- = '\0';
++ if (tmp_field == last_field) {
++ r->status = HTTP_BAD_REQUEST;
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03453)
++ "Request header field name was empty");
++ return;
++ }
++ }
++ else /* Using strict RFC7230 parsing */
++ {
++ /* Ensure valid token chars before ':' per RFC 7230 3.2.4 */
++ value = (char *)ap_scan_http_token(last_field);
++ if ((value == last_field) || *value != ':') {
++ r->status = HTTP_BAD_REQUEST;
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02426)
++ "Request header field name is malformed: "
++ "%.*s", (int)LOG_NAME_MAX_LEN, last_field);
++ return;
+ }
+
+- /* Strip LWS after field-value: */
+- tmp_field = last_field + last_len - 1;
+- while (tmp_field > value
+- && (*tmp_field == ' ' || *tmp_field == '\t')) {
+- *tmp_field-- = '\0';
++ *value++ = '\0'; /* NUL-terminate last_field name at ':' */
++
++ while (*value == ' ' || *value == '\t') {
++ ++value; /* Skip LWS of value */
+ }
+
+- apr_table_addn(r->headers_in, last_field, value);
++ /* Find invalid, non-HT ctrl char, or the trailing NULL */
++ tmp_field = (char *)ap_scan_http_field_content(value);
+
+- /* reset the alloc_len so that we'll allocate a new
+- * buffer if we have to do any more folding: we can't
+- * use the previous buffer because its contents are
+- * now part of r->headers_in
++ /* Reject value for all garbage input (CTRLs excluding HT)
++ * e.g. only VCHAR / SP / HT / obs-text are allowed per
++ * RFC7230 3.2.6 - leave all more explicit rule enforcement
++ * for specific header handler logic later in the cycle
+ */
+- alloc_len = 0;
++ if (*tmp_field != '\0') {
++ r->status = HTTP_BAD_REQUEST;
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02427)
++ "Request header value is malformed: "
++ "%.*s", (int)LOG_NAME_MAX_LEN, value);
++ return;
++ }
++ }
++
++ apr_table_addn(r->headers_in, last_field, value);
+
+- } /* end if current line is not a continuation starting with tab */
++ /* This last_field header is now stored in headers_in,
++ * resume processing of the current input line.
++ */
+ }
+
+- /* Found a blank line, stop. */
++ /* Found the terminating empty end-of-headers line, stop. */
+ if (len == 0) {
+ break;
+ }
+
+- /* Keep track of this line so that we can parse it on
+- * the next loop iteration. (In the folded case, last_field
+- * has been updated already.)
++ /* Keep track of this new header line so that we can extend it across
++ * any obs-fold or parse it on the next loop iteration. We referenced
++ * our previously allocated buffer in r->headers_in,
++ * so allocate a fresh buffer if required.
+ */
+- if (!folded) {
+- last_field = field;
+- last_len = len;
+- }
++ alloc_len = 0;
++ last_field = field;
++ last_len = len;
+ }
+
+ /* Combine multiple message-header fields with the same
+@@ -899,7 +1197,7 @@
+ request_rec *r;
+ apr_pool_t *p;
+ const char *expect;
+- int access_status = HTTP_OK;
++ int access_status;
+ apr_bucket_brigade *tmp_bb;
+ apr_socket_t *csd;
+ apr_interval_time_t cur_timeout;
+@@ -958,35 +1256,39 @@
+
+ /* Get the request... */
+ if (!read_request_line(r, tmp_bb)) {
+- if (r->status == HTTP_REQUEST_URI_TOO_LARGE
+- || r->status == HTTP_BAD_REQUEST) {
++ switch (r->status) {
++ case HTTP_REQUEST_URI_TOO_LARGE:
++ case HTTP_BAD_REQUEST:
++ case HTTP_VERSION_NOT_SUPPORTED:
++ case HTTP_NOT_IMPLEMENTED:
+ if (r->status == HTTP_REQUEST_URI_TOO_LARGE) {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00565)
+ "request failed: client's request-line exceeds LimitRequestLine (longer than %d)",
+ r->server->limit_req_line);
+ }
+ else if (r->method == NULL) {
+- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00566)
+- "request failed: invalid characters in URI");
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00566)
++ "request failed: malformed request line");
+ }
+- ap_send_error_response(r, 0);
++ access_status = r->status;
++ r->status = HTTP_OK;
++ ap_die(access_status, r);
+ ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
+ ap_run_log_transaction(r);
++ r = NULL;
+ apr_brigade_destroy(tmp_bb);
+ goto traceout;
+- }
+- else if (r->status == HTTP_REQUEST_TIME_OUT) {
+- ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
+- if (!r->connection->keepalives) {
++ case HTTP_REQUEST_TIME_OUT:
++ ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, NULL);
++ if (!r->connection->keepalives)
+ ap_run_log_transaction(r);
+- }
+ apr_brigade_destroy(tmp_bb);
+ goto traceout;
++ default:
++ apr_brigade_destroy(tmp_bb);
++ r = NULL;
++ goto traceout;
+ }
+-
+- apr_brigade_destroy(tmp_bb);
+- r = NULL;
+- goto traceout;
+ }
+
+ /* We may have been in keep_alive_timeout mode, so toggle back
+@@ -1003,7 +1305,7 @@
+ if (!r->assbackwards) {
+ ap_get_mime_headers_core(r, tmp_bb);
+ if (r->status != HTTP_OK) {
+- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00567)
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00567)
+ "request failed: error reading the headers");
+ ap_send_error_response(r, 0);
+ ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
+@@ -1021,25 +1323,6 @@
+ apr_table_unset(r->headers_in, "Content-Length");
+ }
+ }
+- else {
+- if (r->header_only) {
+- /*
+- * Client asked for headers only with HTTP/0.9, which doesn't send
+- * headers! Have to dink things just to make sure the error message
+- * comes through...
+- */
+- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00568)
+- "client sent invalid HTTP/0.9 request: HEAD %s",
+- r->uri);
+- r->header_only = 0;
+- r->status = HTTP_BAD_REQUEST;
+- ap_send_error_response(r, 0);
+- ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
+- ap_run_log_transaction(r);
+- apr_brigade_destroy(tmp_bb);
+- goto traceout;
+- }
+- }
+
+ apr_brigade_destroy(tmp_bb);
+
+@@ -1071,7 +1354,7 @@
+ * a Host: header, and the server MUST respond with 400 if it doesn't.
+ */
+ access_status = HTTP_BAD_REQUEST;
+- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00569)
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00569)
+ "client sent HTTP/1.1 request without hostname "
+ "(see RFC2616 section 14.23): %s", r->uri);
+ }
+diff -uap httpd-2.4.6/server/util.c.cve8743 httpd-2.4.6/server/util.c
+--- httpd-2.4.6/server/util.c.cve8743
++++ httpd-2.4.6/server/util.c
+@@ -79,7 +79,7 @@
+ * char in here and get it to work, because if char is signed then it
+ * will first be sign extended.
+ */
+-#define TEST_CHAR(c, f) (test_char_table[(unsigned)(c)] & (f))
++#define TEST_CHAR(c, f) (test_char_table[(unsigned char)(c)] & (f))
+
+ /* Win32/NetWare/OS2 need to check for both forward and back slashes
+ * in ap_getparents() and ap_escape_url.
+@@ -1449,6 +1449,37 @@
+ return find_list_item(p, line, tok, AP_ETAG_WEAK);
+ }
+
++/* Scan a string for HTTP VCHAR/obs-text characters including HT and SP
++ * (as used in header values, for example, in RFC 7230 section 3.2)
++ * returning the pointer to the first non-HT ASCII ctrl character.
++ */
++AP_DECLARE(const char *) ap_scan_http_field_content(const char *ptr)
++{
++ for ( ; !TEST_CHAR(*ptr, T_HTTP_CTRLS); ++ptr) ;
++
++ return ptr;
++}
++
++/* Scan a string for HTTP token characters, returning the pointer to
++ * the first non-token character.
++ */
++AP_DECLARE(const char *) ap_scan_http_token(const char *ptr)
++{
++ for ( ; !TEST_CHAR(*ptr, T_HTTP_TOKEN_STOP); ++ptr) ;
++
++ return ptr;
++}
++
++/* Scan a string for visible ASCII (0x21-0x7E) or obstext (0x80+)
++ * and return a pointer to the first ctrl/space character encountered.
++ */
++AP_DECLARE(const char *) ap_scan_vchar_obstext(const char *ptr)
++{
++ for ( ; TEST_CHAR(*ptr, T_VCHAR_OBSTEXT); ++ptr) ;
++
++ return ptr;
++}
++
+ /* Retrieve a token, spacing over it and returning a pointer to
+ * the first non-white byte afterwards. Note that these tokens
+ * are delimited by semis and commas; and can also be delimited
+diff -uap httpd-2.4.6/server/vhost.c.cve8743 httpd-2.4.6/server/vhost.c
+--- httpd-2.4.6/server/vhost.c.cve8743
++++ httpd-2.4.6/server/vhost.c
+@@ -685,6 +685,116 @@
+ * run-time vhost matching functions
+ */
+
++static apr_status_t fix_hostname_v6_literal(request_rec *r, char *host)
++{
++ char *dst;
++ int double_colon = 0;
++
++ for (dst = host; *dst; dst++) {
++ if (apr_isxdigit(*dst)) {
++ if (apr_isupper(*dst)) {
++ *dst = apr_tolower(*dst);
++ }
++ }
++ else if (*dst == ':') {
++ if (*(dst + 1) == ':') {
++ if (double_colon)
++ return APR_EINVAL;
++ double_colon = 1;
++ }
++ else if (*(dst + 1) == '.') {
++ return APR_EINVAL;
++ }
++ }
++ else if (*dst == '.') {
++ /* For IPv4-mapped IPv6 addresses like ::FFFF:129.144.52.38 */
++ if (*(dst + 1) == ':' || *(dst + 1) == '.')
++ return APR_EINVAL;
++ }
++ else {
++ return APR_EINVAL;
++ }
++ }
++ return APR_SUCCESS;
++}
++
++static apr_status_t fix_hostname_non_v6(request_rec *r, char *host)
++{
++ char *dst;
++
++ for (dst = host; *dst; dst++) {
++ if (apr_islower(*dst)) {
++ /* leave char unchanged */
++ }
++ else if (*dst == '.') {
++ if (*(dst + 1) == '.') {
++ return APR_EINVAL;
++ }
++ }
++ else if (apr_isupper(*dst)) {
++ *dst = apr_tolower(*dst);
++ }
++ else if (*dst == '/' || *dst == '\\') {
++ return APR_EINVAL;
++ }
++ }
++ /* strip trailing gubbins */
++ if (dst > host && dst[-1] == '.') {
++ dst[-1] = '\0';
++ }
++ return APR_SUCCESS;
++}
++
++/*
++ * If strict mode ever becomes the default, this should be folded into
++ * fix_hostname_non_v6()
++ */
++static apr_status_t strict_hostname_check(request_rec *r, char *host)
++{
++ char *ch;
++ int is_dotted_decimal = 1, leading_zeroes = 0, dots = 0;
++
++ for (ch = host; *ch; ch++) {
++ if (!apr_isascii(*ch)) {
++ goto bad;
++ }
++ else if (apr_isalpha(*ch) || *ch == '-') {
++ is_dotted_decimal = 0;
++ }
++ else if (ch[0] == '.') {
++ dots++;
++ if (ch[1] == '0' && apr_isdigit(ch[2]))
++ leading_zeroes = 1;
++ }
++ else if (!apr_isdigit(*ch)) {
++ /* also takes care of multiple Host headers by denying commas */
++ goto bad;
++ }
++ }
++ if (is_dotted_decimal) {
++ if (host[0] == '.' || (host[0] == '0' && apr_isdigit(host[1])))
++ leading_zeroes = 1;
++ if (leading_zeroes || dots != 3) {
++ /* RFC 3986 7.4 */
++ goto bad;
++ }
++ }
++ else {
++ /* The top-level domain must start with a letter (RFC 1123 2.1) */
++ while (ch > host && *ch != '.')
++ ch--;
++ if (ch[0] == '.' && ch[1] != '\0' && !apr_isalpha(ch[1]))
++ goto bad;
++ }
++ return APR_SUCCESS;
++
++bad:
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02415)
++ "[strict] Invalid host name '%s'%s%.6s",
++ host, *ch ? ", problem near: " : "", ch);
++ return APR_EINVAL;
++}
++
+ /* Lowercase and remove any trailing dot and/or :port from the hostname,
+ * and check that it is sane.
+ *
+@@ -698,79 +808,90 @@
+ * Instead we just check for filesystem metacharacters: directory
+ * separators / and \ and sequences of more than one dot.
+ */
+-static void fix_hostname(request_rec *r)
++static int fix_hostname(request_rec *r, const char *host_header,
++ unsigned http_conformance)
+ {
++ const char *src;
+ char *host, *scope_id;
+- char *dst;
+ apr_port_t port;
+ apr_status_t rv;
+ const char *c;
++ int is_v6literal = 0;
++ int strict = (http_conformance != AP_HTTP_CONFORMANCE_UNSAFE);
+
+- /* According to RFC 2616, Host header field CAN be blank. */
+- if (!*r->hostname) {
+- return;
++ src = host_header ? host_header : r->hostname;
++
++ /* According to RFC 2616, Host header field CAN be blank */
++ if (!*src) {
++ return is_v6literal;
+ }
+
+ /* apr_parse_addr_port will interpret a bare integer as a port
+ * which is incorrect in this context. So treat it separately.
+ */
+- for (c = r->hostname; apr_isdigit(*c); ++c);
+- if (!*c) { /* pure integer */
+- return;
++ for (c = src; apr_isdigit(*c); ++c);
++ if (!*c) {
++ /* pure integer */
++ if (strict) {
++ /* RFC 3986 7.4 */
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02416)
++ "[strict] purely numeric host names not allowed: %s",
++ src);
++ goto bad_nolog;
++ }
++ r->hostname = src;
++ return is_v6literal;
++ }
++
++ if (host_header) {
++ rv = apr_parse_addr_port(&host, &scope_id, &port, src, r->pool);
++ if (rv != APR_SUCCESS || scope_id)
++ goto bad;
++ if (port) {
++ /* Don't throw the Host: header's port number away:
++ save it in parsed_uri -- ap_get_server_port() needs it! */
++ /* @@@ XXX there should be a better way to pass the port.
++ * Like r->hostname, there should be a r->portno
++ */
++ r->parsed_uri.port = port;
++ r->parsed_uri.port_str = apr_itoa(r->pool, (int)port);
++ }
++ if (host_header[0] == '[')
++ is_v6literal = 1;
++ }
++ else {
++ /*
++ * Already parsed, surrounding [ ] (if IPv6 literal) and :port have
++ * already been removed.
++ */
++ host = apr_pstrdup(r->pool, r->hostname);
++ if (ap_strchr(host, ':') != NULL)
++ is_v6literal = 1;
+ }
+
+- rv = apr_parse_addr_port(&host, &scope_id, &port, r->hostname, r->pool);
+- if (rv != APR_SUCCESS || scope_id) {
+- goto bad;
++ if (is_v6literal) {
++ rv = fix_hostname_v6_literal(r, host);
+ }
+-
+- if (port) {
+- /* Don't throw the Host: header's port number away:
+- save it in parsed_uri -- ap_get_server_port() needs it! */
+- /* @@@ XXX there should be a better way to pass the port.
+- * Like r->hostname, there should be a r->portno
+- */
+- r->parsed_uri.port = port;
+- r->parsed_uri.port_str = apr_itoa(r->pool, (int)port);
++ else {
++ rv = fix_hostname_non_v6(r, host);
++ if (strict && rv == APR_SUCCESS)
++ rv = strict_hostname_check(r, host);
+ }
++ if (rv != APR_SUCCESS)
++ goto bad;
+
+- /* if the hostname is an IPv6 numeric address string, it was validated
+- * already; otherwise, further validation is needed
+- */
+- if (r->hostname[0] != '[') {
+- for (dst = host; *dst; dst++) {
+- if (apr_islower(*dst)) {
+- /* leave char unchanged */
+- }
+- else if (*dst == '.') {
+- if (*(dst + 1) == '.') {
+- goto bad;
+- }
+- }
+- else if (apr_isupper(*dst)) {
+- *dst = apr_tolower(*dst);
+- }
+- else if (*dst == '/' || *dst == '\\') {
+- goto bad;
+- }
+- }
+- /* strip trailing gubbins */
+- if (dst > host && dst[-1] == '.') {
+- dst[-1] = '\0';
+- }
+- }
+ r->hostname = host;
+- return;
++ return is_v6literal;
+
+ bad:
+- r->status = HTTP_BAD_REQUEST;
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00550)
+ "Client sent malformed Host header: %s",
+- r->hostname);
+- return;
++ src);
++bad_nolog:
++ r->status = HTTP_BAD_REQUEST;
++ return is_v6literal;
+ }
+
+-
+ /* return 1 if host matches ServerName or ServerAliases */
+ static int matches_aliases(server_rec *s, const char *host)
+ {
+@@ -980,15 +1101,76 @@
+ }
+ }
+
++static APR_INLINE const char *construct_host_header(request_rec *r,
++ int is_v6literal)
++{
++ struct iovec iov[5];
++ apr_size_t nvec = 0;
++ /*
++ * We cannot use ap_get_server_name/port here, because we must
++ * ignore UseCanonicalName/Port.
++ */
++ if (is_v6literal) {
++ iov[nvec].iov_base = "[";
++ iov[nvec].iov_len = 1;
++ nvec++;
++ }
++ iov[nvec].iov_base = (void *)r->hostname;
++ iov[nvec].iov_len = strlen(r->hostname);
++ nvec++;
++ if (is_v6literal) {
++ iov[nvec].iov_base = "]";
++ iov[nvec].iov_len = 1;
++ nvec++;
++ }
++ if (r->parsed_uri.port_str) {
++ iov[nvec].iov_base = ":";
++ iov[nvec].iov_len = 1;
++ nvec++;
++ iov[nvec].iov_base = r->parsed_uri.port_str;
++ iov[nvec].iov_len = strlen(r->parsed_uri.port_str);
++ nvec++;
++ }
++ return apr_pstrcatv(r->pool, iov, nvec, NULL);
++}
+
+ AP_DECLARE(void) ap_update_vhost_from_headers(request_rec *r)
+ {
+- /* must set this for HTTP/1.1 support */
+- if (r->hostname || (r->hostname = apr_table_get(r->headers_in, "Host"))) {
+- fix_hostname(r);
+- if (r->status != HTTP_OK)
+- return;
++ core_server_config *conf = ap_get_core_module_config(r->server->module_config);
++ const char *host_header = apr_table_get(r->headers_in, "Host");
++ int is_v6literal = 0;
++ int have_hostname_from_url = 0;
++
++ if (r->hostname) {
++ /*
++ * If there was a host part in the Request-URI, ignore the 'Host'
++ * header.
++ */
++ have_hostname_from_url = 1;
++ is_v6literal = fix_hostname(r, NULL, conf->http_conformance);
++ }
++ else if (host_header != NULL) {
++ is_v6literal = fix_hostname(r, host_header, conf->http_conformance);
++ }
++ if (r->status != HTTP_OK)
++ return;
++
++ if (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE) {
++ /*
++ * If we have both hostname from an absoluteURI and a Host header,
++ * we must ignore the Host header (RFC 2616 5.2).
++ * To enforce this, we reset the Host header to the value from the
++ * request line.
++ */
++ if (have_hostname_from_url && host_header != NULL) {
++ const char *repl = construct_host_header(r, is_v6literal);
++ apr_table_set(r->headers_in, "Host", repl);
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02417)
++ "Replacing host header '%s' with host '%s' given "
++ "in the request uri", host_header, repl);
++ }
+ }
++
+ /* check if we tucked away a name_chain */
+ if (r->connection->vhost_lookup_data) {
+ if (r->hostname)
diff --git a/SOURCES/httpd-2.4.6-r1570327.patch b/SOURCES/httpd-2.4.6-r1570327.patch
new file mode 100644
index 0000000..35cdce8
--- /dev/null
+++ b/SOURCES/httpd-2.4.6-r1570327.patch
@@ -0,0 +1,20 @@
+# ./pullrev.sh 1570327
+http://svn.apache.org/viewvc?view=revision&revision=1570327
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1327624
+
+--- httpd-2.4.6/server/mpm_unix.c
++++ httpd-2.4.6/server/mpm_unix.c
+@@ -742,7 +742,12 @@
+ * readers stranded (a number of them could be tied up for
+ * a while serving time-consuming requests)
+ */
++ /* Recall: we only worry about IDLE child processes here */
+ for (i = 0; i < num && rv == APR_SUCCESS; i++) {
++ if (ap_scoreboard_image->servers[i][0].status != SERVER_READY ||
++ ap_scoreboard_image->servers[i][0].pid == 0) {
++ continue;
++ }
+ rv = dummy_connection(pod);
+ }
+ }
diff --git a/SOURCES/httpd-2.4.6-r1587053.patch b/SOURCES/httpd-2.4.6-r1587053.patch
new file mode 100644
index 0000000..5bee0b1
--- /dev/null
+++ b/SOURCES/httpd-2.4.6-r1587053.patch
@@ -0,0 +1,92 @@
+diff --git a/modules/proxy/mod_proxy_wstunnel.c b/modules/proxy/mod_proxy_wstunnel.c
+index 525109a..eb34eee 100644
+--- a/modules/proxy/mod_proxy_wstunnel.c
++++ b/modules/proxy/mod_proxy_wstunnel.c
+@@ -103,10 +103,12 @@ static int proxy_wstunnel_transfer(request_rec *r, conn_rec *c_i, conn_rec *c_o,
+ rv = ap_get_brigade(c_i->input_filters, bb, AP_MODE_READBYTES,
+ APR_NONBLOCK_READ, AP_IOBUFSIZE);
+ if (rv == APR_SUCCESS) {
+- if (c_o->aborted)
++ if (c_o->aborted) {
+ return APR_EPIPE;
+- if (APR_BRIGADE_EMPTY(bb))
++ }
++ if (APR_BRIGADE_EMPTY(bb)){
+ break;
++ }
+ #ifdef DEBUGGING
+ len = -1;
+ apr_brigade_length(bb, 0, &len);
+@@ -178,7 +180,6 @@ static int ap_proxy_wstunnel_request(apr_pool_t *p, request_rec *r,
+ conn_rec *c = r->connection;
+ apr_socket_t *sock = conn->sock;
+ conn_rec *backconn = conn->connection;
+- int client_error = 0;
+ char *buf;
+ apr_bucket_brigade *header_brigade;
+ apr_bucket *e;
+@@ -224,7 +225,7 @@ static int ap_proxy_wstunnel_request(apr_pool_t *p, request_rec *r,
+
+ pollfd.p = p;
+ pollfd.desc_type = APR_POLL_SOCKET;
+- pollfd.reqevents = APR_POLLIN;
++ pollfd.reqevents = APR_POLLIN | APR_POLLHUP;
+ pollfd.desc.s = sock;
+ pollfd.client_data = NULL;
+ apr_pollset_add(pollset, &pollfd);
+@@ -237,6 +238,9 @@ static int ap_proxy_wstunnel_request(apr_pool_t *p, request_rec *r,
+ r->proto_output_filters = c->output_filters;
+ r->input_filters = c->input_filters;
+ r->proto_input_filters = c->input_filters;
++ /* This handler should take care of the entire connection; make it so that
++ * nothing else is attempted on the connection after returning. */
++ c->keepalive = AP_CONN_CLOSE;
+
+ remove_reqtimeout(r->input_filters);
+
+@@ -257,26 +261,28 @@ static int ap_proxy_wstunnel_request(apr_pool_t *p, request_rec *r,
+
+ if (cur->desc.s == sock) {
+ pollevent = cur->rtnevents;
+- if (pollevent & APR_POLLIN) {
++ if (pollevent & (APR_POLLIN | APR_POLLHUP)) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02446)
+ "sock was readable");
+ rv = proxy_wstunnel_transfer(r, backconn, c, bb, "sock");
+ }
+- else if ((pollevent & APR_POLLERR)
+- || (pollevent & APR_POLLHUP)) {
++ else if (pollevent & APR_POLLERR) {
+ rv = APR_EPIPE;
+ ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, APLOGNO(02447)
+- "err/hup on backconn");
++ "err on backconn");
+ }
+- if (rv != APR_SUCCESS)
+- client_error = 1;
+ }
+ else if (cur->desc.s == client_socket) {
+ pollevent = cur->rtnevents;
+- if (pollevent & APR_POLLIN) {
++ if (pollevent & (APR_POLLIN | APR_POLLHUP)) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02448)
+ "client was readable");
+ rv = proxy_wstunnel_transfer(r, c, backconn, bb, "client");
++ } else if (pollevent & APR_POLLERR) {
++ rv = APR_EPIPE;
++ c->aborted = 1;
++ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(02607)
++ "error on client conn");
+ }
+ }
+ else {
+@@ -294,9 +300,6 @@ static int ap_proxy_wstunnel_request(apr_pool_t *p, request_rec *r,
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
+ "finished with poll() - cleaning up");
+
+- if (client_error) {
+- return HTTP_INTERNAL_SERVER_ERROR;
+- }
+ return OK;
+ }
+
diff --git a/SOURCES/httpd-2.4.6-r1631119.patch b/SOURCES/httpd-2.4.6-r1631119.patch
new file mode 100644
index 0000000..f0f48e4
--- /dev/null
+++ b/SOURCES/httpd-2.4.6-r1631119.patch
@@ -0,0 +1,16 @@
+# ./pullrev.sh 1631119
+http://svn.apache.org/viewvc?view=revision&revision=1631119
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1415257
+
+--- httpd-2.4.6/modules/ldap/util_ldap.c
++++ httpd-2.4.6/modules/ldap/util_ldap.c
+@@ -1824,7 +1824,7 @@
+ * combination, which might be reused unintentionally next time this
+ * connection is used from the connection pool.
+ */
+- ldc->must_rebind = 0;
++ ldc->must_rebind = 1;
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "LDC %pp used for authn, must be rebound", ldc);
+ }
+
diff --git a/SOURCES/welcome.conf b/SOURCES/welcome.conf
index c1b6c11..5d1e452 100644
--- a/SOURCES/welcome.conf
+++ b/SOURCES/welcome.conf
@@ -16,7 +16,3 @@
Alias /.noindex.html /usr/share/httpd/noindex/index.html
-Alias /noindex/css/bootstrap.min.css /usr/share/httpd/noindex/css/bootstrap.min.css
-Alias /noindex/css/open-sans.css /usr/share/httpd/noindex/css/open-sans.css
-Alias /images/apache_pb.gif /usr/share/httpd/noindex/images/apache_pb.gif
-Alias /images/poweredby.png /usr/share/httpd/noindex/images/poweredby.png
diff --git a/SPECS/httpd.spec b/SPECS/httpd.spec
index 7ce38f6..4280492 100644
--- a/SPECS/httpd.spec
+++ b/SPECS/httpd.spec
@@ -4,7 +4,7 @@
%define mmn 20120211
%define oldmmnisa %{mmn}-%{__isa_name}-%{__isa_bits}
%define mmnisa %{mmn}%{__isa_name}%{__isa_bits}
-%define vstring CentOS
+%define vstring %(source /etc/os-release; echo ${REDHAT_SUPPORT_PRODUCT})
# Drop automatic provides for module DSOs
%{?filter_setup:
@@ -15,10 +15,10 @@
Summary: Apache HTTP Server
Name: httpd
Version: 2.4.6
-Release: 45%{?dist}
+Release: 45%{?dist}.4
URL: http://httpd.apache.org/
Source0: http://www.apache.org/dist/httpd/httpd-%{version}.tar.bz2
-Source1: centos-noindex.tar.gz
+Source1: index.html
Source2: httpd.logrotate
Source3: httpd.sysconf
Source4: httpd-ssl-pass-dialog
@@ -124,6 +124,9 @@ Patch103: httpd-2.4.6-dhparams-free.patch
Patch104: httpd-2.4.6-r1651658.patch
Patch105: httpd-2.4.6-r1560093.patch
Patch106: httpd-2.4.6-r1748212.patch
+Patch107: httpd-2.4.6-r1570327.patch
+Patch108: httpd-2.4.6-r1631119.patch
+Patch109: httpd-2.4.6-r1587053.patch
# Security fixes
Patch200: httpd-2.4.6-CVE-2013-6438.patch
Patch201: httpd-2.4.6-CVE-2014-0098.patch
@@ -137,6 +140,9 @@ Patch208: httpd-2.4.6-CVE-2014-3581.patch
Patch209: httpd-2.4.6-CVE-2015-3185.patch
Patch210: httpd-2.4.6-CVE-2015-3183.patch
Patch211: httpd-2.4.6-CVE-2016-5387.patch
+Patch212: httpd-2.4.6-CVE-2016-8743.patch
+Patch213: httpd-2.4.6-CVE-2016-0736.patch
+Patch214: httpd-2.4.6-CVE-2016-2161.patch
License: ASL 2.0
Group: System Environment/Daemons
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
@@ -323,6 +329,9 @@ rm modules/ssl/ssl_engine_dh.c
%patch104 -p1 -b .r1651658
%patch105 -p1 -b .r1560093
%patch106 -p1 -b .r1748212
+%patch107 -p1 -b .r1570327
+%patch108 -p1 -b .r1631119
+%patch109 -p1 -b .r1587053
%patch200 -p1 -b .cve6438
%patch201 -p1 -b .cve0098
@@ -336,6 +345,9 @@ rm modules/ssl/ssl_engine_dh.c
%patch209 -p1 -b .cve3185
%patch210 -p1 -b .cve3183
%patch211 -p1 -b .cve5387
+%patch212 -p1 -b .cve8743
+%patch213 -p1 -b .cve0736
+%patch214 -p1 -b .cve2161
# Patch in the vendor string and the release string
sed -i '/^#define PLATFORM/s/Unix/%{vstring}/' os/unix/os.h
@@ -489,10 +501,8 @@ EOF
# Handle contentdir
mkdir $RPM_BUILD_ROOT%{contentdir}/noindex
-tar xzf $RPM_SOURCE_DIR/centos-noindex.tar.gz \
- -C $RPM_BUILD_ROOT%{contentdir}/noindex/ \
- --strip-components=1
-
+install -m 644 -p $RPM_SOURCE_DIR/index.html \
+ $RPM_BUILD_ROOT%{contentdir}/noindex/index.html
rm -rf %{contentdir}/htdocs
# remove manual sources
@@ -515,7 +525,7 @@ rm -v $RPM_BUILD_ROOT%{docroot}/html/*.html \
$RPM_BUILD_ROOT%{docroot}/cgi-bin/*
# Symlink for the powered-by-$DISTRO image:
-ln -s ../noindex/images/poweredby.png \
+ln -s ../../pixmaps/poweredby.png \
$RPM_BUILD_ROOT%{contentdir}/icons/poweredby.png
# symlinks for /etc/httpd
@@ -701,7 +711,7 @@ rm -rf $RPM_BUILD_ROOT
%{contentdir}/error/README
%{contentdir}/error/*.var
%{contentdir}/error/include/*.html
-%{contentdir}/noindex/*
+%{contentdir}/noindex/index.html
%dir %{docroot}
%dir %{docroot}/cgi-bin
@@ -767,11 +777,20 @@ rm -rf $RPM_BUILD_ROOT
%{_sysconfdir}/rpm/macros.httpd
%changelog
-* Thu Nov 03 2016 CentOS Sources - 2.4.6-45.el7.centos
-- Remove index.html, add centos-noindex.tar.gz
-- change vstring
-- change symlink for poweredby.png
-- update welcome.conf with proper aliases
+* Wed Mar 08 2017 Luboš Uhliarik - 2.4.6-45.4
+- Resolves: #1396197 - Backport: mod_proxy_wstunnel - AH02447: err/hup
+ on backconn
+
+* Tue Feb 14 2017 Joe Orton - 2.4.6-45.3
+- prefork: fix delay completing graceful restart (#1327624)
+- mod_ldap: fix authz regression, failing to rebind (#1415257)
+
+* Tue Feb 14 2017 Joe Orton - 2.4.6-45.2
+- updated patch for CVE-2016-8743
+
+* Mon Jan 30 2017 Luboš Uhliarik - 2.4.6-45.1
+- Resolves: #1412975 - CVE-2016-0736 CVE-2016-2161 CVE-2016-8743 httpd: various
+ flaws
* Wed Aug 03 2016 Luboš Uhliarik - 2.4.6-45
- RFE: run mod_rewrite external mapping program as non-root (#1316900)
|
---|
|
---|