[Pkg-fedora-ds-maintainers] [libapache2-mod-nss] 74/156: Merge in changes from http://svn.apache.org/viewvc?view=rev&revision=290965

Timo Aaltonen tjaalton-guest at moszumanska.debian.org
Wed Jul 2 13:55:30 UTC 2014


This is an automated email from the git hooks/post-receive script.

tjaalton-guest pushed a commit to branch master
in repository libapache2-mod-nss.

commit a2c56687f17bdefe258c63b17264d80be2cbd44c
Author: rcritten <>
Date:   Wed Aug 9 20:11:45 2006 +0000

    Merge in changes from http://svn.apache.org/viewvc?view=rev&revision=290965
    
    Implement a (bounded) buffer of request body data to provide a limited
    but safe fix for the mod_nss renegotiation-vs-requests-with-bodies
    bug:
    
    * mod_nss.h (nss_io_buffer_fill): Add prototype.
    
    * nss_engine_io.c (nss_io_buffer_fill,
      nss_io_filter_buffer): New functions.
    
    * nss_engine_kernel.c (nss_hook_Access): If a renegotiation is needed,
      and the request has a non-zero content-length, or a t-e header (and
      100-continue was not requested), call nss_io_buffer_fill to set aside
      the request body data if possible, then proceed with the negotiation.
    
    PR: 12355
---
 mod_nss.h           |   4 ++
 nss_engine_io.c     | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 nss_engine_kernel.c |  90 ++++++++-------------------
 3 files changed, 206 insertions(+), 64 deletions(-)

diff --git a/mod_nss.h b/mod_nss.h
index 30c74fa..e60281e 100644
--- a/mod_nss.h
+++ b/mod_nss.h
@@ -447,6 +447,10 @@ apr_file_t  *nss_util_ppopen(server_rec *, apr_pool_t *, const char *,
 void         nss_util_ppclose(server_rec *, apr_pool_t *, apr_file_t *);
 char        *nss_util_readfilter(server_rec *, apr_pool_t *, const char *,
                                  const char * const *);
+/* ssl_io_buffer_fill fills the setaside buffering of the HTTP request
+ * to allow an SSL renegotiation to take place. */
+int          nss_io_buffer_fill(request_rec *r);
+
 int nss_rand_seed(server_rec *s, apr_pool_t *p, ssl_rsctx_t nCtx, char *prefix);
 
 /* Pass Phrase Handling */
diff --git a/nss_engine_io.c b/nss_engine_io.c
index 64641d8..8010f89 100644
--- a/nss_engine_io.c
+++ b/nss_engine_io.c
@@ -602,6 +602,7 @@ static apr_status_t nss_io_filter_error(ap_filter_t *f,
 }
 
 static const char nss_io_filter[] = "NSS SSL/TLS Filter";
+static const char nss_io_buffer[] = "NSS SSL/TLS Buffer";
 
 static apr_status_t nss_filter_io_shutdown(nss_filter_ctx_t *filter_ctx,
                                            conn_rec *c,
@@ -916,6 +917,180 @@ static void nss_io_output_create(nss_filter_ctx_t *filter_ctx, conn_rec *c)
     return;
 }
 
+/* 128K maximum buffer size by default. */
+#ifndef SSL_MAX_IO_BUFFER
+#define SSL_MAX_IO_BUFFER (128 * 1024)
+#endif
+
+struct modnss_buffer_ctx {
+    apr_bucket_brigade *bb;
+};
+
+int nss_io_buffer_fill(request_rec *r)
+{
+    conn_rec *c = r->connection;
+    struct modnss_buffer_ctx *ctx;
+    apr_bucket_brigade *tempb;
+    apr_off_t total = 0; /* total length buffered */
+    int eos = 0; /* non-zero once EOS is seen */
+    
+    /* Create the context which will be passed to the input filter. */
+    ctx = apr_palloc(r->pool, sizeof *ctx);
+    ctx->bb = apr_brigade_create(r->pool, c->bucket_alloc);
+
+    /* ... and a temporary brigade. */
+    tempb = apr_brigade_create(r->pool, c->bucket_alloc);
+
+    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "filling buffer");
+
+    do {
+        apr_status_t rv;
+        apr_bucket *e, *next;
+
+        /* The request body is read from the protocol-level input
+         * filters; the buffering filter will reinject it from that
+         * level, allowing content/resource filters to run later, if
+         * necessary. */
+
+        rv = ap_get_brigade(r->proto_input_filters, tempb, AP_MODE_READBYTES,
+                            APR_BLOCK_READ, 8192);
+        if (rv) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+                          "could not read request body for SSL buffer");
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+        
+        /* Iterate through the returned brigade: setaside each bucket
+         * into the context's pool and move it into the brigade. */
+        for (e = APR_BRIGADE_FIRST(tempb); 
+             e != APR_BRIGADE_SENTINEL(tempb) && !eos; e = next) {
+            const char *data;
+            apr_size_t len;
+
+            next = APR_BUCKET_NEXT(e);
+
+            if (APR_BUCKET_IS_EOS(e)) {
+                eos = 1;
+            } else if (!APR_BUCKET_IS_METADATA(e)) {
+                rv = apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
+                if (rv != APR_SUCCESS) {
+                    ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+                                  "could not read bucket for SSL buffer");
+                    return HTTP_INTERNAL_SERVER_ERROR;
+                }
+                total += len;
+            }
+                
+            rv = apr_bucket_setaside(e, r->pool);
+            if (rv != APR_SUCCESS) {
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+                              "could not setaside bucket for SSL buffer");
+                return HTTP_INTERNAL_SERVER_ERROR;
+            }
+            
+            APR_BUCKET_REMOVE(e);
+            APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+        }
+
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, 
+                      "total of %" APR_OFF_T_FMT " bytes in buffer, eos=%d",
+                      total, eos);
+
+        /* Fail if this exceeds the maximum buffer size. */
+        if (total > SSL_MAX_IO_BUFFER) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                          "request body exceeds maximum size for SSL buffer");
+            return HTTP_REQUEST_ENTITY_TOO_LARGE;
+        }
+
+    } while (!eos);
+
+    apr_brigade_destroy(tempb);
+
+    /* Insert the filter which will supply the buffered data. */
+    ap_add_input_filter(nss_io_buffer, ctx, r, c);
+
+    return 0;
+}
+
+/* This input filter supplies the buffered request body to the caller
+ * from the brigade stored in f->ctx. */
+static apr_status_t nss_io_filter_buffer(ap_filter_t *f,
+                                         apr_bucket_brigade *bb,
+                                         ap_input_mode_t mode,
+                                         apr_read_type_e block,
+                                         apr_off_t bytes)
+{
+    struct modnss_buffer_ctx *ctx = f->ctx;
+    apr_status_t rv;
+
+    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r,
+                  "read from buffered SSL brigade, mode %d, "
+                  "%" APR_OFF_T_FMT " bytes",
+                  mode, bytes);
+
+    if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE) {
+        return APR_ENOTIMPL;
+    }
+
+    if (mode == AP_MODE_READBYTES) {
+        apr_bucket *e;
+
+        /* Partition the buffered brigade. */
+        rv = apr_brigade_partition(ctx->bb, bytes, &e);
+        if (rv && rv != APR_INCOMPLETE) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r,
+                          "could not partition buffered SSL brigade");
+            ap_remove_input_filter(f);
+            return rv;
+        }
+
+        /* If the buffered brigade contains less then the requested
+         * length, just pass it all back. */
+        if (rv == APR_INCOMPLETE) {
+            APR_BRIGADE_CONCAT(bb, ctx->bb);
+        } else {
+            apr_bucket *d = APR_BRIGADE_FIRST(ctx->bb);
+
+            e = APR_BUCKET_PREV(e);
+            
+            /* Unsplice the partitioned segment and move it into the
+             * passed-in brigade; no convenient way to do this with
+             * the APR_BRIGADE_* macros. */
+            APR_RING_UNSPLICE(d, e, link);
+            APR_RING_SPLICE_HEAD(&bb->list, d, e, apr_bucket, link);
+        }
+    }
+    else {
+        /* Split a line into the passed-in brigade. */
+        rv = apr_brigade_split_line(bb, ctx->bb, mode, bytes);
+
+        if (rv) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r,
+                          "could not split line from buffered SSL brigade");
+            ap_remove_input_filter(f);
+            return rv;
+        }
+    }
+
+    if (APR_BRIGADE_EMPTY(ctx->bb)) {
+        apr_bucket *e = APR_BRIGADE_LAST(bb);
+        
+        /* Ensure that the brigade is terminated by an EOS if the
+         * buffered request body has been entirely consumed. */
+        if (e == APR_BRIGADE_SENTINEL(bb) || !APR_BUCKET_IS_EOS(e)) {
+            e = apr_bucket_eos_create(f->c->bucket_alloc);
+            APR_BRIGADE_INSERT_TAIL(bb, e);
+        }
+
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r,
+                      "buffered SSL brigade now exhausted; removing filter");
+        ap_remove_input_filter(f);
+    }
+
+    return APR_SUCCESS;
+}
+
 static void nss_io_input_add_filter(nss_filter_ctx_t *filter_ctx, conn_rec *c,
                                     PRFileDesc *ssl)
 {
@@ -962,6 +1137,7 @@ void nss_io_filter_register(apr_pool_t *p)
 {
     ap_register_input_filter  (nss_io_filter, nss_io_filter_input,  NULL, AP_FTYPE_CONNECTION + 5);
     ap_register_output_filter (nss_io_filter, nss_io_filter_output, NULL, AP_FTYPE_CONNECTION + 5);  
+    ap_register_input_filter  (nss_io_buffer, nss_io_filter_buffer, NULL, AP_FTYPE_PROTOCOL - 1);
     return; 
 }
 
diff --git a/nss_engine_kernel.c b/nss_engine_kernel.c
index baa8c49..6a73007 100644
--- a/nss_engine_kernel.c
+++ b/nss_engine_kernel.c
@@ -312,73 +312,35 @@ int nss_hook_Access(request_rec *r)
         }
     }
 
-    /*
-     * SSL renegotiations in conjunction with HTTP
-     * requests using the POST method are not supported.
-     *
-     * Background:
-     *
-     * 1. When the client sends a HTTP/HTTPS request, Apache's core code
-     * reads only the request line ("METHOD /path HTTP/x.y") and the
-     * attached MIME headers ("Foo: bar") up to the terminating line ("CR
-     * LF"). An attached request body (for instance the data of a POST
-     * method) is _NOT_ read. Instead it is read by mod_cgi's content
-     * handler and directly passed to the CGI script.
-     *
-     * 2. mod_ssl supports per-directory re-configuration of SSL parameters.
-     * This is implemented by performing an SSL renegotiation of the
-     * re-configured parameters after the request is read, but before the
-     * response is sent. In more detail: the renegotiation happens after the
-     * request line and MIME headers were read, but _before_ the attached
-     * request body is read. The reason simply is that in the HTTP protocol
-     * usually there is no acknowledgment step between the headers and the
-     * body (there is the 100-continue feature and the chunking facility
-     * only), so Apache has no API hook for this step.
-     *
-     * 3. the problem now occurs when the client sends a POST request for
-     * URL /foo via HTTPS the server and the server has SSL parameters
-     * re-configured on a per-URL basis for /foo. Then mod_ssl has to
-     * perform an SSL renegotiation after the request was read and before
-     * the response is sent. But the problem is the pending POST body data
-     * in the receive buffer of SSL (which Apache still has not read - it's
-     * pending until mod_cgi sucks it in). When mod_ssl now tries to perform
-     * the renegotiation the pending data leads to an I/O error.
-     *
-     * Solution Idea:
-     *
-     * There are only two solutions: Either to simply state that POST
-     * requests to URLs with SSL re-configurations are not allowed, or to
-     * renegotiate really after the _complete_ request (i.e. including
-     * the POST body) was read. Obviously the latter would be preferred,
-     * but it cannot be done easily inside Apache, because as already
-     * mentioned, there is no API step between the body reading and the body
-     * processing. And even when we mod_ssl would hook directly into the
-     * loop of mod_cgi, we wouldn't solve the problem for other handlers, of
-     * course. So the only general solution is to suck in the pending data
-     * of the request body from the OpenSSL BIO into the Apache BUFF. Then
-     * the renegotiation can be done and after this step Apache can proceed
-     * processing the request as before. 
-     * 
-     * Solution Implementation:
-     * 
-     * We cannot simply suck in the data via an SSL_read-based loop because of
-     * HTTP chunking. Instead we _have_ to use the Apache API for this step which
-     * is aware of HTTP chunking. So the trick is to suck in the pending request
-     * data via the Apache API (which uses Apache's BUFF code and in the
-     * background mod_ssl's I/O glue code) and re-inject it later into the Apache
-     * BUFF code again. This way the data flows twice through the Apache BUFF, of
-     * course. But this way the solution doesn't depend on any Apache specifics
-     * and is fully transparent to Apache modules. 
+    /* If a renegotiation is now required for this location, and the
+     * request includes a message body (and the client has not
+     * requested a "100 Continue" response), then the client will be
+     * streaming the request body over the wire already.  In that
+     * case, it is not possible to stop and perform a new SSL
+     * handshake immediately; once the SSL library moves to the
+     * "accept" state, it will reject the SSL packets which the client
+     * is sending for the request body.
      * 
-     * !! BUT ALL THIS IS STILL NOT RE-IMPLEMENTED FOR APACHE 2.0 !! 
+     * To allow authentication to complete in this auth hook, the
+     * solution used here is to fill a (bounded) buffer with the
+     * request body, and then to reinject that request body later.
      */
-    if (renegotiate && !renegotiate_quick && (r->method_number == M_POST)) {
-        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
-                     "SSL Re-negotiation in conjunction "
-                     "with POST method not supported!"
-                     "hint: try NSSOptions +OptRenegotiate");
+    if (renegotiate && !renegotiate_quick
+        && (apr_table_get(r->headers_in, "transfer-encoding")
+            || (apr_table_get(r->headers_in, "content-length")
+                && strcmp(apr_table_get(r->headers_in, "content-length"), "0")))
+        && !r->expecting_100) {
+        int rv;
 
-        return HTTP_METHOD_NOT_ALLOWED;
+        /* Fill the I/O buffer with the request body if possible. */
+        rv = nss_io_buffer_fill(r);
+
+        if (rv) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                          "could not buffer message body to allow "
+                          "SSL renegotiation to proceed");
+            return rv;
+        }
     }
 
     /*

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-fedora-ds/libapache2-mod-nss.git



More information about the Pkg-fedora-ds-maintainers mailing list