You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ji...@apache.org on 2014/07/17 20:20:47 UTC

svn commit: r1611426 - in /httpd/httpd/branches/2.2.x: ./ CHANGES STATUS docs/manual/mod/mod_deflate.xml modules/filters/mod_deflate.c

Author: jim
Date: Thu Jul 17 18:20:46 2014
New Revision: 1611426

URL: http://svn.apache.org/r1611426
Log:
Merge r1610501 from trunk:

  *) SECURITY: CVE-2014-0118 (cve.mitre.org)
     mod_deflate: The DEFLATE input filter (inflates request bodies) now
     limits the length and compression ratio of inflated request bodies to avoid
     denial of sevice via highly compressed bodies.  See directives 
     DeflateInflateLimitRequestBody, DeflateInflateRatioLimit,
     and DeflateInflateRatioBurst.

Thanks to Giancarlo Pellegrino and Davide Balzarotti for reporting the issue.

Submitted By: ylavic, covener
Reviewed By: jorton, covener, jim



Submitted by: covener
Reviewed/backported by: jim

Modified:
    httpd/httpd/branches/2.2.x/   (props changed)
    httpd/httpd/branches/2.2.x/CHANGES
    httpd/httpd/branches/2.2.x/STATUS
    httpd/httpd/branches/2.2.x/docs/manual/mod/mod_deflate.xml
    httpd/httpd/branches/2.2.x/modules/filters/mod_deflate.c

Propchange: httpd/httpd/branches/2.2.x/
------------------------------------------------------------------------------
  Merged /httpd/httpd/trunk:r1610501

Modified: httpd/httpd/branches/2.2.x/CHANGES
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.2.x/CHANGES?rev=1611426&r1=1611425&r2=1611426&view=diff
==============================================================================
--- httpd/httpd/branches/2.2.x/CHANGES [utf-8] (original)
+++ httpd/httpd/branches/2.2.x/CHANGES [utf-8] Thu Jul 17 18:20:46 2014
@@ -1,6 +1,13 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache 2.2.28
 
+  *) SECURITY: CVE-2014-0118 (cve.mitre.org)
+     mod_deflate: The DEFLATE input filter (inflates request bodies) now
+     limits the length and compression ratio of inflated request bodies to avoid
+     denial of sevice via highly compressed bodies.  See directives
+     DeflateInflateLimitRequestBody, DeflateInflateRatioLimit,
+     and DeflateInflateRatioBurst. [Yann Ylavic, Eric Covener]
+
   *) SECURITY: CVE-2014-0231 (cve.mitre.org)
      mod_cgid: Fix a denial of service against CGI scripts that do
      not consume stdin that could lead to lingering HTTPD child processes

Modified: httpd/httpd/branches/2.2.x/STATUS
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.2.x/STATUS?rev=1611426&r1=1611425&r2=1611426&view=diff
==============================================================================
--- httpd/httpd/branches/2.2.x/STATUS (original)
+++ httpd/httpd/branches/2.2.x/STATUS Thu Jul 17 18:20:46 2014
@@ -99,16 +99,6 @@ RELEASE SHOWSTOPPERS:
 PATCHES ACCEPTED TO BACKPORT FROM TRUNK:
   [ start all new proposals below, under PATCHES PROPOSED. ]
 
-   * SECURITY: CVE-2014-0118 (cve.mitre.org)
-     mod_deflate: The DEFLATE input filter (inflates request bodies) now
-     limits the length and compression ratio of inflated request bodies to avoid
-     denial of sevice via highly compressed bodies.  See directives
-     DeflateInflateLimitRequestBody, DeflateInflateRatioLimit,
-     and DeflateInflateRatioBurst. [Yann Ylavic, Eric Covener]
- 
-     trunk patch: http://svn.apache.org/r1610501
-     2.2.x patch: http://people.apache.org/~covener/patches/httpd-2.2.x-deflate_limitrequestbody.diff
-     +1: covener, ylavic, jorton
 
    * mod_deflate: Don't fail when asked to flush inflated data to the user-agent
                   and that coincides with the end of stream ("Zlib error flushing

Modified: httpd/httpd/branches/2.2.x/docs/manual/mod/mod_deflate.xml
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.2.x/docs/manual/mod/mod_deflate.xml?rev=1611426&r1=1611425&r2=1611426&view=diff
==============================================================================
--- httpd/httpd/branches/2.2.x/docs/manual/mod/mod_deflate.xml (original)
+++ httpd/httpd/branches/2.2.x/docs/manual/mod/mod_deflate.xml Thu Jul 17 18:20:46 2014
@@ -332,5 +332,58 @@ client</description>
 </directivesynopsis>
 
 
+<directivesynopsis>
+<name>DeflateInflateLimitRequestBody</name>
+<description>Maximum size of inflated request bodies</description>
+<syntax>DeflateInflateLimitRequestBody<var>value</var></syntax>
+<default>None, but LimitRequestBody applies after deflation</default>
+<contextlist><context>server config</context><context>virtual host</context>
+<context>directory</context><context>.htaccess</context></contextlist>
+<compatibility>2.2.28 and later</compatibility>
+
+<usage>
+    <p>The <directive>DeflateInflateLimitRequestBody</directive> directive 
+        specifies the maximum size of an inflated request body. If it is unset,
+        <directive module="core">LimitRequestBody</directive> is applied to the
+        inflated body.</p>
+</usage>
+</directivesynopsis>
+
+<directivesynopsis>
+<name>DeflateInflateRatioLimit</name>
+<description>Maximum inflation ratio for request bodies</description>
+<syntax>DeflateInflateRatioLimit <var>value</var></syntax>
+<default>200</default>
+<contextlist><context>server config</context><context>virtual host</context>
+<context>directory</context><context>.htaccess</context></contextlist>
+<compatibility>2.2.28 and later</compatibility>
+
+<usage>
+    <p>The <directive>DeflateInflateRatioLimit</directive> directive 
+        specifies the maximum ratio of deflated to inflated size of an 
+        inflated request body. This ratio is checked as the body is
+        streamed in, and if crossed more than 
+        <directive>DeflateInflateRatioBurst</directive> times the request
+        will be terminated.</p>
+</usage>
+</directivesynopsis>
+
+<directivesynopsis>
+<name>DeflateInflateRatioBurst</name>
+<description>Maximum number of times the inflation ratio for request bodies 
+             can be crossed</description>
+<syntax>DeflateInflateRatioBurst <var>value</var></syntax>
+<default>3</default>
+<contextlist><context>server config</context><context>virtual host</context>
+<context>directory</context><context>.htaccess</context></contextlist>
+<compatibility>2.2.28 and later</compatibility>
+
+<usage>
+    <p>The <directive>DeflateInflateRatioBurst</directive> directive 
+       specifies the maximum number of times the 
+       <directive>DeflateInflateRatioLimit</directive> cab be crossed before 
+       terminating the request.</p>
+</usage>
+</directivesynopsis>
 </modulesynopsis>
 

Modified: httpd/httpd/branches/2.2.x/modules/filters/mod_deflate.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.2.x/modules/filters/mod_deflate.c?rev=1611426&r1=1611425&r2=1611426&view=diff
==============================================================================
--- httpd/httpd/branches/2.2.x/modules/filters/mod_deflate.c (original)
+++ httpd/httpd/branches/2.2.x/modules/filters/mod_deflate.c Thu Jul 17 18:20:46 2014
@@ -37,6 +37,7 @@
 #include "httpd.h"
 #include "http_config.h"
 #include "http_log.h"
+#include "http_core.h"
 #include "apr_lib.h"
 #include "apr_strings.h"
 #include "apr_general.h"
@@ -51,6 +52,9 @@
 static const char deflateFilterName[] = "DEFLATE";
 module AP_MODULE_DECLARE_DATA deflate_module;
 
+#define AP_INFLATE_RATIO_LIMIT 200
+#define AP_INFLATE_RATIO_BURST 3
+
 typedef struct deflate_filter_config_t
 {
     int windowSize;
@@ -62,6 +66,12 @@ typedef struct deflate_filter_config_t
     char *note_output_name;
 } deflate_filter_config;
 
+typedef struct deflate_dirconf_t {
+    apr_off_t inflate_limit;
+    int ratio_limit,
+        ratio_burst;
+} deflate_dirconf_t;
+
 /* RFC 1952 Section 2.3 defines the gzip header:
  *
  * +---+---+---+---+---+---+---+---+---+---+
@@ -193,6 +203,14 @@ static void *create_deflate_server_confi
     return c;
 }
 
+static void *create_deflate_dirconf(apr_pool_t *p, char *dummy)
+{
+    deflate_dirconf_t *dc = apr_pcalloc(p, sizeof(*dc));
+    dc->ratio_limit = AP_INFLATE_RATIO_LIMIT;
+    dc->ratio_burst = AP_INFLATE_RATIO_BURST;
+    return dc;
+}
+
 static const char *deflate_set_window_size(cmd_parms *cmd, void *dummy,
                                            const char *arg)
 {
@@ -284,6 +302,55 @@ static const char *deflate_set_compressi
     return NULL;
 }
 
+
+static const char *deflate_set_inflate_limit(cmd_parms *cmd, void *dirconf,
+                                      const char *arg)
+{
+    deflate_dirconf_t *dc = (deflate_dirconf_t*) dirconf;
+    char *errp;
+
+    if (APR_SUCCESS != apr_strtoff(&dc->inflate_limit, arg, &errp, 10)) {
+        return "DeflateInflateLimitRequestBody is not parsable.";
+    }
+    if (*errp || dc->inflate_limit < 0) {
+        return "DeflateInflateLimitRequestBody requires a non-negative integer.";
+    }
+
+    return NULL;
+}
+
+static const char *deflate_set_inflate_ratio_limit(cmd_parms *cmd,
+                                                   void *dirconf,
+                                                   const char *arg)
+{
+    deflate_dirconf_t *dc = (deflate_dirconf_t*) dirconf;
+    int i;
+
+    i = atoi(arg);
+    if (i <= 0)
+        return "DeflateInflateRatioLimit must be positive";
+
+    dc->ratio_limit = i;
+
+    return NULL;
+}
+
+static const char *deflate_set_inflate_ratio_burst(cmd_parms *cmd,
+                                                   void *dirconf,
+                                                   const char *arg)
+{
+    deflate_dirconf_t *dc = (deflate_dirconf_t*) dirconf;
+    int i;
+
+    i = atoi(arg);
+    if (i <= 0)
+        return "DeflateInflateRatioBurst must be positive";
+
+    dc->ratio_burst = i;
+
+    return NULL;
+}
+
 typedef struct deflate_ctx_t
 {
     z_stream stream;
@@ -294,8 +361,26 @@ typedef struct deflate_ctx_t
     unsigned char *validation_buffer;
     apr_size_t validation_buffer_length;
     int inflate_init;
+    int ratio_hits;
+    apr_off_t inflate_total;
 } deflate_ctx;
 
+/* Check whether the (inflate) ratio exceeds the configured limit/burst. */
+static int check_ratio(request_rec *r, deflate_ctx *ctx,
+                       const deflate_dirconf_t *dc)
+{
+    if (ctx->stream.total_in) {
+        int ratio = ctx->stream.total_out / ctx->stream.total_in;
+        if (ratio < dc->ratio_limit) {
+            ctx->ratio_hits = 0;
+        }
+        else if (++ctx->ratio_hits > dc->ratio_burst) {
+            return 0;
+        }
+    }
+    return 1;
+}
+
 /* Number of validation bytes (CRC and length) after the compressed data */
 #define VALIDATION_SIZE 8
 /* Do not update ctx->crc, see comment in flush_libz_buffer */
@@ -744,6 +829,8 @@ static apr_status_t deflate_in_filter(ap
     int zRC;
     apr_status_t rv;
     deflate_filter_config *c;
+    deflate_dirconf_t *dc;
+    apr_off_t inflate_limit;
 
     /* just get out of the way of things we don't want. */
     if (mode != AP_MODE_READBYTES) {
@@ -751,6 +838,7 @@ static apr_status_t deflate_in_filter(ap
     }
 
     c = ap_get_module_config(r->server->module_config, &deflate_module);
+    dc = ap_get_module_config(r->per_dir_config, &deflate_module);
 
     if (!ctx) {
         char deflate_hdr[10];
@@ -803,11 +891,13 @@ static apr_status_t deflate_in_filter(ap
         if (len != 10 ||
             deflate_hdr[0] != deflate_magic[0] ||
             deflate_hdr[1] != deflate_magic[1]) {
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Failed to inflate input: wrong/partial magic bytes");
             return APR_EGENERAL;
         }
 
         /* We can't handle flags for now. */
         if (deflate_hdr[3] != 0) {
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Failed to inflate input: cannot handle deflate flags");
             return APR_EGENERAL;
         }
 
@@ -831,6 +921,12 @@ static apr_status_t deflate_in_filter(ap
         apr_brigade_cleanup(ctx->bb);
     }
 
+    inflate_limit = dc->inflate_limit; 
+    if (inflate_limit == 0) { 
+        /* The core is checking the deflated body, we'll check the inflated */
+        inflate_limit = ap_get_limit_req_body(f->r);
+    }
+
     if (APR_BRIGADE_EMPTY(ctx->proc_bb)) {
         rv = ap_get_brigade(f->next, ctx->bb, mode, block, readbytes);
 
@@ -863,6 +959,17 @@ static apr_status_t deflate_in_filter(ap
 
                 ctx->stream.next_out = ctx->buffer;
                 len = c->bufferSize - ctx->stream.avail_out;
+ 
+                ctx->inflate_total += len;
+                if (inflate_limit && ctx->inflate_total > inflate_limit) { 
+                    inflateEnd(&ctx->stream);
+                    ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, 
+                            "Inflated content length of %" APR_OFF_T_FMT
+                            " is larger than the configured limit"
+                            " of %" APR_OFF_T_FMT, 
+                            ctx->inflate_total, inflate_limit);
+                    return APR_ENOSPC;
+                }
 
                 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
                 tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
@@ -891,6 +998,26 @@ static apr_status_t deflate_in_filter(ap
                     ctx->stream.next_out = ctx->buffer;
                     len = c->bufferSize - ctx->stream.avail_out;
 
+                      ctx->inflate_total += len;
+                      if (inflate_limit && ctx->inflate_total > inflate_limit) { 
+                          inflateEnd(&ctx->stream);
+                          ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
+                                  "Inflated content length of %" APR_OFF_T_FMT
+                                  " is larger than the configured limit"
+                                  " of %" APR_OFF_T_FMT, 
+                                  ctx->inflate_total, inflate_limit);
+                          return APR_ENOSPC;
+                      }
+
+                      if (!check_ratio(r, ctx, dc)) {
+                          inflateEnd(&ctx->stream);
+                          ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, 
+                                  "Inflated content ratio is larger than the "
+                                  "configured limit %i by %i time(s)",
+                                  dc->ratio_limit, dc->ratio_burst);
+                          return APR_EINVAL;
+                      }
+
                     ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
                     tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
                                                       NULL, f->c->bucket_alloc);
@@ -1003,6 +1130,7 @@ static apr_status_t inflate_out_filter(a
     int zRC;
     apr_status_t rv;
     deflate_filter_config *c;
+    deflate_dirconf_t *dc;
 
     /* Do nothing if asked to filter nothing. */
     if (APR_BRIGADE_EMPTY(bb)) {
@@ -1010,6 +1138,7 @@ static apr_status_t inflate_out_filter(a
     }
 
     c = ap_get_module_config(r->server->module_config, &deflate_module);
+    dc = ap_get_module_config(r->per_dir_config, &deflate_module);
 
     if (!ctx) {
 
@@ -1272,6 +1401,14 @@ static apr_status_t inflate_out_filter(a
         while (ctx->stream.avail_in != 0) {
             if (ctx->stream.avail_out == 0) {
 
+                if (!check_ratio(r, ctx, dc)) {
+                    ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, 
+                            "Inflated content ratio is larger than the "
+                            "configured limit %i by %i time(s)",
+                            dc->ratio_limit, dc->ratio_burst);
+                    return APR_EINVAL;
+                }
+
                 ctx->stream.next_out = ctx->buffer;
                 len = c->bufferSize - ctx->stream.avail_out;
 
@@ -1346,12 +1483,20 @@ static const command_rec deflate_filter_
                   "Set the Deflate Memory Level (1-9)"),
     AP_INIT_TAKE1("DeflateCompressionLevel", deflate_set_compressionlevel, NULL, RSRC_CONF,
                   "Set the Deflate Compression Level (1-9)"),
+    AP_INIT_TAKE1("DeflateInflateLimitRequestBody", deflate_set_inflate_limit, NULL, OR_ALL,
+                  "Set a limit on size of inflated input"),
+    AP_INIT_TAKE1("DeflateInflateRatioLimit", deflate_set_inflate_ratio_limit, NULL, OR_ALL,
+                  "Set the inflate ratio limit above which inflation is "
+                  "aborted (default: " APR_STRINGIFY(AP_INFLATE_RATIO_LIMIT) ")"),
+    AP_INIT_TAKE1("DeflateInflateRatioBurst", deflate_set_inflate_ratio_burst, NULL, OR_ALL,
+                  "Set the maximum number of following inflate ratios above limit "
+                  "(default: " APR_STRINGIFY(AP_INFLATE_RATIO_BURST) ")"),
     {NULL}
 };
 
 module AP_MODULE_DECLARE_DATA deflate_module = {
     STANDARD20_MODULE_STUFF,
-    NULL,                         /* dir config creater */
+    create_deflate_dirconf,       /* dir config creater */
     NULL,                         /* dir merger --- default is to override */
     create_deflate_server_config, /* server config */
     NULL,                         /* merge server config */