Author: joes
Date: Tue Mar 15 06:46:10 2005
New Revision: 157544

URL: http://svn.apache.org/viewcvs?view=rev&rev=157544
Log:
Protect against the possibility of apache2_body_get
going quadratic on a "single-character-brigade" attack
against ap_http_filter.  apreq_hook_find_param ensures 
the prefetch behavior is always (at worst) O(n).


Modified:
    httpd/apreq/branches/multi-env-unstable/include/apreq_parser.h
    httpd/apreq/branches/multi-env-unstable/library/parser.c
    
httpd/apreq/branches/multi-env-unstable/module/apache2/apreq_private_apache2.h
    httpd/apreq/branches/multi-env-unstable/module/apache2/handle.c

Modified: httpd/apreq/branches/multi-env-unstable/include/apreq_parser.h
URL: 
http://svn.apache.org/viewcvs/httpd/apreq/branches/multi-env-unstable/include/apreq_parser.h?view=diff&r1=157543&r2=157544
==============================================================================
--- httpd/apreq/branches/multi-env-unstable/include/apreq_parser.h (original)
+++ httpd/apreq/branches/multi-env-unstable/include/apreq_parser.h Tue Mar 15 
06:46:10 2005
@@ -266,6 +266,19 @@
  */
 APREQ_DECLARE_HOOK(apreq_hook_discard_brigade);
 
+/**
+ * Special purpose utility for locating a parameter
+ * during parsing.  The hook's ctx shoud be initialized
+ * to a const char *, which is a pointer to the desired 
+ * param name.  The hook's ctx will be reassigned to the 
+ * first param found.
+ *
+ * @remarks When used, this should always be the first hook
+ * invoked, so add it manually as parser->hook instead of 
+ * using apreq_parser_add_hook.
+ */
+APREQ_DECLARE_HOOK(apreq_hook_find_param);
+
 
 #ifdef __cplusplus
 }

Modified: httpd/apreq/branches/multi-env-unstable/library/parser.c
URL: 
http://svn.apache.org/viewcvs/httpd/apreq/branches/multi-env-unstable/library/parser.c?view=diff&r1=157543&r2=157544
==============================================================================
--- httpd/apreq/branches/multi-env-unstable/library/parser.c (original)
+++ httpd/apreq/branches/multi-env-unstable/library/parser.c Tue Mar 15 
06:46:10 2005
@@ -333,3 +333,15 @@
     return APR_SUCCESS;
 }
 
+APREQ_DECLARE_HOOK(apreq_hook_find_param)
+{
+    const char *key = hook->ctx;
+    int is_final = (bb == NULL) || APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb));
+    apr_status_t s = (hook->next == NULL)
+        ? APR_SUCCESS : apreq_hook_run(hook->next, param, bb);
+
+    if (is_final && strcasecmp(key, param->v.name) == 0)
+        hook->ctx = param;
+
+    return s;
+}

Modified: 
httpd/apreq/branches/multi-env-unstable/module/apache2/apreq_private_apache2.h
URL: 
http://svn.apache.org/viewcvs/httpd/apreq/branches/multi-env-unstable/module/apache2/apreq_private_apache2.h?view=diff&r1=157543&r2=157544
==============================================================================
--- 
httpd/apreq/branches/multi-env-unstable/module/apache2/apreq_private_apache2.h 
(original)
+++ 
httpd/apreq/branches/multi-env-unstable/module/apache2/apreq_private_apache2.h 
Tue Mar 15 06:46:10 2005
@@ -23,6 +23,7 @@
     apr_bucket_brigade *spool; /* copied prefetch data for downstream filters 
*/
     apreq_parser_t     *parser;
     apreq_hook_t       *hook_queue;
+    apreq_hook_t       *find_param;
     apr_table_t        *body;
     apr_status_t        body_status;
     apr_status_t        filter_error;

Modified: httpd/apreq/branches/multi-env-unstable/module/apache2/handle.c
URL: 
http://svn.apache.org/viewcvs/httpd/apreq/branches/multi-env-unstable/module/apache2/handle.c?view=diff&r1=157543&r2=157544
==============================================================================
--- httpd/apreq/branches/multi-env-unstable/module/apache2/handle.c (original)
+++ httpd/apreq/branches/multi-env-unstable/module/apache2/handle.c Tue Mar 15 
06:46:10 2005
@@ -179,6 +179,7 @@
     ap_filter_t *f = get_apreq_filter(env);
     struct filter_ctx *ctx;
     const char *val;
+    apreq_hook_t *h;
 
     if (f->ctx == NULL)
         apreq_filter_make_context(f);
@@ -194,35 +195,57 @@
             return NULL;
         apreq_filter_prefetch(f, APREQ_DEFAULT_READ_BLOCK_SIZE);
 
+
     case APR_INCOMPLETE:
 
         val = apr_table_get(ctx->body, name);
         if (val != NULL)
             return apreq_value_to_param(val);
 
+        /* Not seen yet, so we need to scan for 
+           param while prefetching the body */
+
+        if (ctx->find_param == NULL)
+            ctx->find_param = apreq_hook_make(f->r->pool, 
+                                              apreq_hook_find_param, 
+                                              NULL, NULL);
+        h = ctx->find_param;
+        h->next = ctx->parser->hook;
+        ctx->parser->hook = h;
+        *(const char **)&h->ctx = name;
+
         do {
-            /* riff on Duff's device */
             apreq_filter_prefetch(f, APREQ_DEFAULT_READ_BLOCK_SIZE);
+            if (h->ctx != name) {
+                ctx->parser->hook = h->next;
+                return h->ctx;
+            }
+        } while (ctx->body_status == APR_INCOMPLETE);
 
-    case APR_SUCCESS:
+        ctx->parser->hook = h->next;
+        return NULL;
 
-            val = apr_table_get(ctx->body, name);
-            if (val != NULL)
-                return apreq_value_to_param(val);
 
-        } while (ctx->body_status == APR_INCOMPLETE);
+    case APR_SUCCESS:
 
-        break;
+        val = apr_table_get(ctx->body, name);
+        if (val != NULL)
+            return apreq_value_to_param(val);
+        return NULL;
 
     default:
 
-        if (ctx->body != NULL) {
-            val = apr_table_get(ctx->body, name);
-            if (val != NULL)
-                return apreq_value_to_param(val);
-        }
+        if (ctx->body == NULL)
+            return NULL;
+
+        val = apr_table_get(ctx->body, name);
+        if (val != NULL)
+            return apreq_value_to_param(val);
+        return NULL;
+
     }
 
+    /* not reached */
     return NULL;
 }
 


Reply via email to