#
# denial_of_service_fix.patch for Apache 2.0.44
#
# Authoritative location:
#
# http://www.apache.org/dist/httpd/patches/apply_to_2.0.44/
#
# From httpd-2.0/CHANGES:
#
#  *) SECURITY [CAN-2003-0132]: Close a Denial of Service vulnerability
#     identified by David Endler <DEndler@iDefense.com> on all platforms.
#     Details embargoed until their announcement on 8 April 2003.
#
# Simplify and shorten the code path for scanning request headers.
# This is the scanning logic that was subject to an unlimited search
# for a non-blank line between requests.  These lines each consumed
# 80 bytes - until memory was exhausted in the DoS attack identified
# by David Endler <DEndler@iDefense.com>.
#
# This patch must be applied to all platforms.  
#
# Also, a fix for seg faults with huge headers (interwoven with 
# the primary patch, and not worth detangling.)
#
#  1.121.2.3 (1.129) +3 -0      httpd-2.0/server/protocol.c
#  1.121.2.4 (1.130) +31 -81    httpd-2.0/server/protocol.c
#
Index: server/protocol.c
===================================================================
RCS file: /home/cvs/httpd-2.0/server/protocol.c,v
retrieving revision 1.121
diff -u -r1.121 protocol.c
--- server/protocol.c	19 Nov 2002 19:32:38 -0000	1.121
+++ server/protocol.c	10 Apr 2003 16:59:43 -0000
@@ -251,6 +251,7 @@
     char *pos, *last_char = *s;
     int do_alloc = (*s == NULL), saw_eos = 0;
 
+    for (;;) {
     apr_brigade_cleanup(bb);
     rv = ap_get_brigade(r->input_filters, bb, AP_MODE_GETLINE,
                         APR_BLOCK_READ, 0);
@@ -290,6 +291,7 @@
 
         /* Would this overrun our buffer?  If so, we'll die. */
         if (n < bytes_handled + len) {
+            *read = bytes_handled;
             return APR_ENOSPC;
         }
 
@@ -330,67 +332,9 @@
         bytes_handled += len;
     }
 
-    /* We likely aborted early before reading anything or we read no
-     * data.  Technically, this might be success condition.  But,
-     * probably means something is horribly wrong.  For now, we'll
-     * treat this as APR_SUCCESS, but it may be worth re-examining.
-     */
-    if (bytes_handled == 0) {
-        *read = 0;
-        return APR_SUCCESS;
-    }
-
-    /* If we didn't get a full line of input, try again. */
-    if (*last_char != APR_ASCII_LF) {
-        /* Do we have enough space? We may be full now. */
-        if (bytes_handled < n) {
-            apr_size_t next_size, next_len;
-            char *tmp;
-
-            /* If we're doing the allocations for them, we have to
-             * give ourselves a NULL and copy it on return.
-             */
-            if (do_alloc) {
-                tmp = NULL;
-            } else {
-                /* We're not null terminated yet. */
-                tmp = last_char + 1;
-            }
-
-            next_size = n - bytes_handled;
-
-            rv = ap_rgetline_core(&tmp, next_size, &next_len, r, fold, bb);
-
-            if (rv != APR_SUCCESS) {
-                return rv;
-            }
-
-            /* XXX this code appears to be dead because the filter chain
-             * seems to read until it sees a LF or an error.  If it ever
-             * comes back to life, we need to make sure that:
-             * - we really alloc enough space for the trailing null
-             * - we don't allow the tail trimming code to run more than
-             *   once
-             */
-            if (do_alloc && next_len > 0) {
-                char *new_buffer;
-                apr_size_t new_size = bytes_handled + next_len;
-
-                /* Again we need to alloc an extra two bytes for LF, null */
-                new_buffer = apr_palloc(r->pool, new_size);
-
-                /* Copy what we already had. */
-                memcpy(new_buffer, *s, bytes_handled);
-                memcpy(new_buffer + bytes_handled, tmp, next_len);
-                current_alloc = new_size;
-                *s = new_buffer;
-            }
-
-            bytes_handled += next_len;
-            last_char = *s + bytes_handled - 1;
-        }
-        else {
-            return APR_ENOSPC;
+        /* If we got a full line of input, stop reading */
+        if (last_char && (*last_char == APR_ASCII_LF)) {
+            break;
         }
     }
 
@@ -429,6 +373,7 @@
      * Note that if an EOS was seen, we know we can't have another line.
      */
     if (fold && bytes_handled && !saw_eos) {
+        for (;;) {
         const char *str;
         apr_size_t len;
         char c;
@@ -445,40 +390,41 @@
         }
 
         if (APR_BRIGADE_EMPTY(bb)) {
-            *read = bytes_handled;
-            return APR_SUCCESS;
+                break;
         }
 
         e = APR_BRIGADE_FIRST(bb);
 
         /* If we see an EOS, don't bother doing anything more. */
         if (APR_BUCKET_IS_EOS(e)) {
-            *read = bytes_handled;
-            return APR_SUCCESS;
+                break;
         }
 
         rv = apr_bucket_read(e, &str, &len, APR_BLOCK_READ);
 
         if (rv != APR_SUCCESS) {
-            apr_brigade_destroy(bb);
+                apr_brigade_cleanup(bb);
             return rv;
         }
 
-        /* When we call destroy, the buckets are deleted, so save that
-         * one character we need.  This simplifies our execution paths
-         * at the cost of one character read.
-         */
-        c = *str;
-
         /* Found one, so call ourselves again to get the next line.
          *
          * FIXME: If the folding line is completely blank, should we
          * stop folding?  Does that require also looking at the next
          * char?
          */
+            /* When we call destroy, the buckets are deleted, so save that
+             * one character we need.  This simplifies our execution paths
+             * at the cost of one character read.
+             */
+            c = *str;
         if (c == APR_ASCII_BLANK || c == APR_ASCII_TAB) {
             /* Do we have enough space? We may be full now. */
-            if (bytes_handled < n) {
+                if (bytes_handled >= n) {
+                    *read = n;
+                    return APR_ENOSPC;
+                }
+                else {
                 apr_size_t next_size, next_len;
                 char *tmp;
 
@@ -494,7 +440,8 @@
 
                 next_size = n - bytes_handled;
 
-                rv = ap_rgetline_core(&tmp, next_size, &next_len, r, fold, bb);
+                    rv = ap_rgetline_core(&tmp, next_size,
+                                          &next_len, r, 0, bb);
 
                 if (rv != APR_SUCCESS) {
                     return rv;
@@ -515,11 +462,11 @@
                     *s = new_buffer;
                 }
 
-                *read = bytes_handled + next_len;
-                return APR_SUCCESS;
+                    bytes_handled += next_len;
+            }
             }
-            else {
-                return APR_ENOSPC;
+            else { /* next character is not tab or space */
+                break;
             }
         }
     }
@@ -644,6 +591,12 @@
     int major = 1, minor = 0;   /* Assume HTTP/1.0 if non-"HTTP" protocol */
     char http[5];
     apr_size_t len;
+    int num_blank_lines = 0;
+    int max_blank_lines = r->server->limit_req_fields;
+
+    if (max_blank_lines <= 0) {
+        max_blank_lines = DEFAULT_LIMIT_REQUEST_FIELDS;
+    }
 
     /* Read past empty lines until we get a real request line,
      * a read error, the connection closes (EOF), or we timeout.
@@ -674,7 +627,7 @@
             r->request_time = apr_time_now();
             return 0;
         }
-    } while (len <= 0);
+    } while ((len <= 0) && (++num_blank_lines < max_blank_lines));
 
     /* we've probably got something to do, ignore graceful restart requests */
 
