From: William A. Rowe Jr Date: Tue, 28 Jun 2005 18:03:25 +0000 (+0000) Subject: Introduce TraceEnable [on|off|extended], fixes non-compliance X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e12138602be4026446ecd895658118d942a9f986;p=thirdparty%2Fapache%2Fhttpd.git Introduce TraceEnable [on|off|extended], fixes non-compliance in mod_proxy which accepted request bodies with TRACE requests. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x@202263 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/htdocs/manual/mod/core.html.en b/htdocs/manual/mod/core.html.en index 1b3b897603a..9ad7b7de350 100644 --- a/htdocs/manual/mod/core.html.en +++ b/htdocs/manual/mod/core.html.en @@ -190,6 +190,8 @@
  • TimeOut
  • +
  • TraceEnable
  • +
  • UseCanonicalName
  • User
  • @@ -3972,6 +3974,40 @@ Syntax OK the timer is not reset when a packet is sent.
    +

    TraceEnable

    + Syntax: TraceEnable + [on|off|extended]
    + Default: TraceEnable + on
    + Context: server config
    + Status: core (Windows, + NetWare)
    + Compatibility: Available only in Apache 1.3.34 + and later + +

    This directive overrides the behavior of TRACE for both + the core server and mod_proxy. The default TraceEnable + on permits TRACE requests per RFC 2616, which disallows + any request body to accompany the request. TraceEnable + off causes the core server and mod_proxy to return + a 405 FORBIDDEN error to the client.

    + +

    Finally, for testing and diagnostic purposes only, request + bodies may be allowed using the non-compliant TraceEnable + extended directive. The core (as an origin server) will + restrict the request body to 64k (plus 8k for chunk headers if + Transfer-Encoding: chunked is used). The core will reflect the + full headers and all chunk headers with the request body. As a + proxy server, the request body is not restricted to 64k. At this + time the Apache 1.3 mod_proxy does not permit chunked request + bodies for any request, including the extended TRACE request.

    +
    +

    UseCanonicalName directive

    diff --git a/htdocs/manual/mod/directives.html.en b/htdocs/manual/mod/directives.html.en index 7f75ba1a204..368462be17e 100644 --- a/htdocs/manual/mod/directives.html.en +++ b/htdocs/manual/mod/directives.html.en @@ -558,6 +558,8 @@
  • TimeOut
  • +
  • TraceEnable
  • +
  • TransferLog
  • diff --git a/src/CHANGES b/src/CHANGES index 1f2cab51339..2c8e5c9b3e9 100644 --- a/src/CHANGES +++ b/src/CHANGES @@ -1,5 +1,12 @@ Changes with Apache 1.3.34 + *) Added TraceEnable [on|off|extended] per-server directive to alter + the behavior of the TRACE method. This addresses a flaw in proxy + conformance to RFC 2616 - previously the proxy server would accept + a TRACE request body although the RFC prohibited it. The default + remains 'TraceEnable on'. + [William Rowe] + *) mod_digest: Fix another nonce string calculation issue. [Eric Covener] diff --git a/src/include/ap_mmn.h b/src/include/ap_mmn.h index a75e52bef4a..d3fa39497b8 100644 --- a/src/include/ap_mmn.h +++ b/src/include/ap_mmn.h @@ -203,6 +203,7 @@ * 19990320.16 - ap_escape_errorlog_item() * 19990320.17 - ap_auth_nonce() and ap_auth_nonce added * in core_dir_config. + * 19990320.18 - trace_enable member added to core server_config */ #define MODULE_MAGIC_COOKIE 0x41503133UL /* "AP13" */ @@ -210,7 +211,7 @@ #ifndef MODULE_MAGIC_NUMBER_MAJOR #define MODULE_MAGIC_NUMBER_MAJOR 19990320 #endif -#define MODULE_MAGIC_NUMBER_MINOR 17 /* 0...n */ +#define MODULE_MAGIC_NUMBER_MINOR 18 /* 0...n */ /* Useful for testing for features. */ #define AP_MODULE_MAGIC_AT_LEAST(major,minor) \ diff --git a/src/include/http_core.h b/src/include/http_core.h index 322b3d94cd0..d0405bdbeb1 100644 --- a/src/include/http_core.h +++ b/src/include/http_core.h @@ -344,8 +344,18 @@ typedef struct { int recursion_limit_set; /* boolean */ int redirect_limit; /* maximum number of internal redirects */ int subreq_limit; /* maximum nesting level of subrequests */ + + /* TRACE control */ + int trace_enable; /* see AP_TRACE_ below */ + } core_server_config; +/* trace_enable options */ +#define AP_TRACE_UNSET -1 +#define AP_TRACE_DISABLE 0 +#define AP_TRACE_ENABLE 1 +#define AP_TRACE_EXTENDED 2 + /* for http_config.c */ CORE_EXPORT(void) ap_core_reorder_directories(pool *, server_rec *); diff --git a/src/main/http_core.c b/src/main/http_core.c index 628f2d169cf..752530d86c2 100644 --- a/src/main/http_core.c +++ b/src/main/http_core.c @@ -341,6 +341,8 @@ static void *create_core_server_config(pool *a, server_rec *s) conf->subreq_limit = 0; conf->recursion_limit_set = 0; + conf->trace_enable = AP_TRACE_UNSET; + return (void *)conf; } @@ -369,6 +371,10 @@ static void *merge_core_server_configs(pool *p, void *basev, void *virtv) ? virt->subreq_limit : base->subreq_limit; + conf->trace_enable = (virt->trace_enable != AP_TRACE_UNSET) + ? virt->trace_enable + : base->trace_enable; + return conf; } @@ -1492,7 +1498,7 @@ CORE_EXPORT_NONSTD(const char *) ap_limit_section(cmd_parms *cmd, void *dummy, int methnum = ap_method_number_of(method); if (methnum == M_TRACE && !tog) { - return "TRACE cannot be controlled by "; + return "TRACE cannot be controlled by , see TraceEnable"; } else if (methnum == M_INVALID) { return ap_pstrcat(cmd->pool, "unknown method \"", method, @@ -3341,6 +3347,28 @@ static const char *set_recursion_limit(cmd_parms *cmd, void *dummy, return NULL; } +static const char *set_trace_enable(cmd_parms *cmd, void *dummy, + const char *arg1) +{ + core_server_config *conf = ap_get_module_config(cmd->server->module_config, + &core_module); + + if (strcasecmp(arg1, "on") == 0) { + conf->trace_enable = AP_TRACE_ENABLE; + } + else if (strcasecmp(arg1, "off") == 0) { + conf->trace_enable = AP_TRACE_DISABLE; + } + else if (strcasecmp(arg1, "extended") == 0) { + conf->trace_enable = AP_TRACE_EXTENDED; + } + else { + return "TraceEnable must be one of 'on', 'off', or 'extended'"; + } + + return NULL; +} + static void log_backtrace(const request_rec *r) { const request_rec *top = r; @@ -3742,6 +3770,8 @@ static const command_rec core_cmds[] = { { "LimitInternalRecursion", set_recursion_limit, NULL, RSRC_CONF, TAKE12, "maximum recursion depth of internal redirects and subrequests"}, +{ "TraceEnable", set_trace_enable, NULL, RSRC_CONF, TAKE1, + "'on' (default), 'off' or 'extended' to trace request body content"}, { NULL } }; diff --git a/src/main/http_protocol.c b/src/main/http_protocol.c index 28bec04335c..fac36529aad 100644 --- a/src/main/http_protocol.c +++ b/src/main/http_protocol.c @@ -1636,12 +1636,15 @@ static void terminate_header(BUFF *client) ap_bputs(CRLF, client); /* Send the terminating empty line */ } -/* Build the Allow field-value from the request handler method mask. - * Note that we always allow TRACE, since it is handled below. +/* Build the Allow header from the request handler method mask. + * Note TRACE is tested on a per-server basis. */ -static char *make_allow(request_rec *r) +static void set_allow_header(request_rec *r) { - return 2 + ap_pstrcat(r->pool, + core_server_config *conf = + ap_get_module_config(r->server->module_config, &core_module); + + char *res = ap_pstrcat(r->pool, (r->allowed & (1 << M_GET)) ? ", GET, HEAD" : "", (r->allowed & (1 << M_POST)) ? ", POST" : "", (r->allowed & (1 << M_PUT)) ? ", PUT" : "", @@ -1656,24 +1659,94 @@ static char *make_allow(request_rec *r) (r->allowed & (1 << M_MOVE)) ? ", MOVE" : "", (r->allowed & (1 << M_LOCK)) ? ", LOCK" : "", (r->allowed & (1 << M_UNLOCK)) ? ", UNLOCK" : "", - ", TRACE", + (conf->trace_enable != AP_TRACE_DISABLE) ? ", TRACE" : "", NULL); + + /* Cowardly attempt to avoid returning an empty Allow: header, + * but no matter how inaccurate, result code 405 demands it. + */ + if (*res) + ap_table_setn(r->headers_out, "Allow", res + 2); + else if (r->status == METHOD_NOT_ALLOWED) + ap_table_setn(r->headers_out, "Allow", ""); } API_EXPORT(int) ap_send_http_trace(request_rec *r) { + core_server_config *conf; int rv; + int body; + char *bodyread, *bodyoff; + long bodylen = 0; + long bodybuf; + long res; /* Get the original request */ while (r->prev) r = r->prev; + conf = ap_get_module_config(r->server->module_config, &core_module); + + if (conf->trace_enable == AP_TRACE_DISABLE) { + ap_table_setn(r->notes, "error-notes", + "TRACE denied by server configuration"); + return HTTP_FORBIDDEN; + } + + if (conf->trace_enable == AP_TRACE_EXTENDED) + body = REQUEST_CHUNKED_PASS; + else + body = REQUEST_NO_BODY; - if ((rv = ap_setup_client_block(r, REQUEST_NO_BODY))) + if ((rv = ap_setup_client_block(r, body))) { + if (rv == HTTP_REQUEST_ENTITY_TOO_LARGE) + ap_table_setn(r->notes, "error-notes", + "TRACE with a request body is not allowed"); return rv; + } + + if (ap_should_client_block(r)) { + + if (r->remaining > 0) { + if (r->remaining > 65536) { + ap_table_setn(r->notes, "error-notes", + "Extended TRACE request bodies cannot exceed 64k\n"); + return HTTP_REQUEST_ENTITY_TOO_LARGE; + } + /* always 32 extra bytes to catch chunk header exceptions */ + bodybuf = r->remaining + 32; + } + else { + /* Add an extra 8192 for chunk headers */ + bodybuf = 73730; + } + + bodyoff = bodyread = ap_palloc(r->pool, bodybuf); + + /* only while we have enough for a chunked header */ + while ((!bodylen || bodybuf >= 32) && + (res = ap_get_client_block(r, bodyoff, bodybuf)) > 0) { + bodylen += res; + bodybuf -= res; + bodyoff += res; + } + if (res > 0 && bodybuf < 32) { + /* discard_rest_of_request_body into our buffer */ + while (ap_get_client_block(r, bodyread, bodylen) > 0) + ; + ap_table_setn(r->notes, "error-notes", + "Extended TRACE request bodies cannot exceed 64k\n"); + return HTTP_REQUEST_ENTITY_TOO_LARGE; + } + + if (res < 0) { + return HTTP_BAD_REQUEST; + } + } ap_hard_timeout("send TRACE", r); r->content_type = "message/http"; + ap_send_http_header(r); #ifdef CHARSET_EBCDIC /* Server-generated response, converted */ @@ -1688,6 +1761,10 @@ API_EXPORT(int) ap_send_http_trace(request_rec *r) ap_send_header_field, (void *) r, r->headers_in, NULL); ap_rputs(CRLF, r); + /* If configured to accept a body, echo the body including chunks */ + if (bodylen) + ap_rwrite(bodyread, bodylen, r); + ap_kill_timeout(r); return OK; } @@ -1704,7 +1781,7 @@ API_EXPORT(int) ap_send_http_options(request_rec *r) ap_basic_http_header(r); ap_table_setn(r->headers_out, "Content-Length", "0"); - ap_table_setn(r->headers_out, "Allow", make_allow(r)); + set_allow_header(r); ap_set_keepalive(r); ap_table_do((int (*) (void *, const char *, const char *)) ap_send_header_field, @@ -2841,7 +2918,7 @@ API_EXPORT(void) ap_send_error_response(request_rec *r, int recursive_error) } if ((status == METHOD_NOT_ALLOWED) || (status == NOT_IMPLEMENTED)) - ap_table_setn(r->headers_out, "Allow", make_allow(r)); + set_allow_header(r); ap_send_http_header(r); diff --git a/src/modules/proxy/proxy_http.c b/src/modules/proxy/proxy_http.c index 1a73a6541ee..7c88e983335 100644 --- a/src/modules/proxy/proxy_http.c +++ b/src/modules/proxy/proxy_http.c @@ -15,6 +15,7 @@ /* HTTP routines for Apache proxy */ +#define CORE_PRIVATE /* To inspect core_server_conf->trace_enable */ #include "mod_proxy.h" #include "http_log.h" #include "http_main.h" @@ -141,6 +142,24 @@ int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url, memset(&server, '\0', sizeof(server)); server.sin_family = AF_INET; + if (r->method_number == M_TRACE) { + core_server_config *coreconf = (core_server_config *) + ap_get_module_config(r->server->module_config, &core_module); + + if (coreconf->trace_enable == AP_TRACE_DISABLE) + return ap_proxyerror(r, HTTP_FORBIDDEN, + "TRACE denied by server configuration"); + + /* Can't test ap_should_client_block, we aren't ready to send + * the client a 100 Continue response till the connection has + * been established + */ + if (coreconf->trace_enable != AP_TRACE_EXTENDED + && (r->read_length || (!r->read_chunked && (r->remaining <= 0)))) + return ap_proxyerror(r, HTTP_REQUEST_ENTITY_TOO_LARGE, + "TRACE with request body is not allowed"); + } + /* We break the URL into host, port, path-search */ urlptr = strstr(url, "://");