]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
Introduce TraceEnable [on|off|extended], fixes non-compliance
authorWilliam A. Rowe Jr <wrowe@apache.org>
Tue, 28 Jun 2005 18:03:25 +0000 (18:03 +0000)
committerWilliam A. Rowe Jr <wrowe@apache.org>
Tue, 28 Jun 2005 18:03:25 +0000 (18:03 +0000)
  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

htdocs/manual/mod/core.html.en
htdocs/manual/mod/directives.html.en
src/CHANGES
src/include/ap_mmn.h
src/include/http_core.h
src/main/http_core.c
src/main/http_protocol.c
src/modules/proxy/proxy_http.c

index 1b3b897603aae5f64c275cc2cfce8fc33f97fa8c..9ad7b7de35098c3e8482354a59e7cf9d5e403067 100644 (file)
 
       <li><a href="#timeout">TimeOut</a></li>
 
+      <li><a href="#timeout">TraceEnable</a></li>
+
       <li><a href="#usecanonicalname">UseCanonicalName</a></li>
 
       <li><a href="#user">User</a></li>
@@ -3972,6 +3974,40 @@ Syntax OK
     the timer is not reset when a packet is sent. 
     <hr />
 
+    <h2><a id="traceenable"
+    name="traceenable">TraceEnable</a></h2>
+    <a href="directive-dict.html#Syntax"
+    rel="Help"><strong>Syntax:</strong></a> TraceEnable
+    <em>[on|off|extended]</em><br />
+     <a href="directive-dict.html#Default"
+    rel="Help"><strong>Default:</strong></a> <code>TraceEnable
+    on</code><br />
+     <a href="directive-dict.html#Context"
+    rel="Help"><strong>Context:</strong></a> server config<br />
+     <a href="directive-dict.html#Status"
+    rel="Help"><strong>Status:</strong></a> core (Windows,
+    NetWare)<br />
+     <strong>Compatibility:</strong> Available only in Apache 1.3.34
+    and later 
+
+    <p>This directive overrides the behavior of TRACE for both
+    the core server and mod_proxy.  The default <code>TraceEnable 
+    on</code> permits TRACE requests per RFC 2616, which disallows
+    any request body to accompany the request.  <code>TraceEnable
+    off</code> causes the core server and mod_proxy to return
+    a 405 FORBIDDEN error to the client.</p>
+
+    <p>Finally, for testing and diagnostic purposes only, request
+    bodies may be allowed using the non-compliant <code>TraceEnable 
+    extended</code> 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.</p>
+    <hr />
+
     <h2><a id="usecanonicalname"
     name="usecanonicalname">UseCanonicalName directive</a></h2>
 
index 7f75ba1a20474ee4824ff92c84d909412679c8d2..368462be17e42ff33e54c1c7236ca88403fea215 100644 (file)
 
       <li><a href="core.html#timeout">TimeOut</a></li>
 
+      <li><a href="core.html#timeout">TraceEnable</a></li>
+
       <li><a
       href="mod_log_config.html#transferlog">TransferLog</a></li>
 
index 1f2cab51339c1319650c2d597247ffdf51351fd6..2c8e5c9b3e9b16fa2cb2c459f28a3512ec31f95b 100644 (file)
@@ -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]
 
index a75e52bef4a5b20a8362371f0f392f5832a6dd52..d3fa39497b8c79304d181a6ae787b38468f5d3fa 100644 (file)
  * 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" */
 #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)          \
index 322b3d94cd02f45ccca7643f7dafb7f49e785302..d0405bdbeb10d1831c11169c9ce1f99ecabe57ff 100644 (file)
@@ -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 *);
 
index 628f2d169cfa224f06647f2c15e06e2ab8c88462..752530d86c2ceae9f98da35447da464812ce23e6 100644 (file)
@@ -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 <Limit>";
+            return "TRACE cannot be controlled by <Limit>, 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 }
 };
 
index 28bec04335cb3f8bd3a124369144b8353d0cd726..fac36529aade56db6c6a30386a7a674a9938fb02 100644 (file)
@@ -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);
 
index 1a73a6541eeded80ca77f1a60c6f1ec5c84442d3..7c88e983335d642e6eb0c2e67f79ec22a5b86db0 100644 (file)
@@ -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, "://");