httpd-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Nathan Friess <natm...@shaw.ca>
Subject [PATCH] SSL, POST, and renegotiation
Date Mon, 10 Jun 2002 22:20:06 GMT
A while back I started working with the httpd sources in attempt to create
the missing code for POSTing over SSL when renegotiation is required.  I
made the necessary changes, tested the code using several 1 to 30 megabyte
binary files, and it seems to work nicely.

The body is sucked up with ap_get_client_block() and related calls, and
added to a brigade, which is placed in core_request_config->bb -- the same
place that ap_get_client_block() uses.  Since mod_cgi[d] now uses the
brigades instead of ap_... calls to get the body, that code needed to be
updated to use the core_request_config->bb brigade first (which makes sense
to me whether used with SSL or not).  ap_discard_request_body() also needed
to be updated for the same reason.

Below is the entire patch against the 'latest' CVS.  If the patch looks
good, then feel free to add it.  If something looks wrong, please let me
know, as I'm interested in continuing to work on this and other useful
changes.

Nathan

Index: modules/generators/mod_cgi.c
===================================================================
RCS file: /home/cvspublic/httpd-2.0/modules/generators/mod_cgi.c,v
retrieving revision 1.141
diff -u -r1.141 mod_cgi.c
--- modules/generators/mod_cgi.c 6 Jun 2002 00:16:59 -0000 1.141
+++ modules/generators/mod_cgi.c 10 Jun 2002 20:41:34 -0000
@@ -604,6 +604,9 @@
     cgi_server_conf *conf;
     apr_status_t rv;
     cgi_exec_info_t e_info;
+    core_request_config *req_cfg =
+      (core_request_config *)ap_get_module_config(r->request_config,
+        &core_module);

     if(strcmp(r->handler, CGI_MAGIC_TYPE) && strcmp(r->handler,
"cgi-script"))
         return DECLINED;
@@ -684,7 +687,7 @@
     /* Transfer any put/post args, CERN style...
      * Note that we already ignore SIGPIPE in the core server.
      */
-    bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+    bb = req_cfg->bb;
     seen_eos = 0;
     child_stopped_reading = 0;
     if (conf->logname) {
@@ -694,12 +697,14 @@
     do {
         apr_bucket *bucket;

-        rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
-                            APR_BLOCK_READ, HUGE_STRING_LEN);
+ if (APR_BRIGADE_EMPTY(bb)) {
+   rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
+         APR_BLOCK_READ, HUGE_STRING_LEN);

-        if (rv != APR_SUCCESS) {
+   if (rv != APR_SUCCESS) {
             return rv;
-        }
+   }
+ }

         APR_BRIGADE_FOREACH(bucket, bb) {
             const char *data;
@@ -746,7 +751,6 @@
                 child_stopped_reading = 1;
             }
         }
-        apr_brigade_cleanup(bb);
     }
     while (!seen_eos);

@@ -764,6 +768,7 @@
         char sbuf[MAX_STRING_LEN];
         int ret;

+ bb = apr_brigade_create(r->pool, c->bucket_alloc);
         b = apr_bucket_pipe_create(script_in, c->bucket_alloc);
         APR_BRIGADE_INSERT_TAIL(bb, b);
         b = apr_bucket_eos_create(c->bucket_alloc);
Index: modules/generators/mod_cgid.c
===================================================================
RCS file: /home/cvspublic/httpd-2.0/modules/generators/mod_cgid.c,v
retrieving revision 1.134
diff -u -r1.134 mod_cgid.c
--- modules/generators/mod_cgid.c 30 May 2002 05:42:46 -0000 1.134
+++ modules/generators/mod_cgid.c 10 Jun 2002 20:41:36 -0000
@@ -1026,6 +1026,9 @@
     int sd;
     char **env;
     apr_file_t *tempsock;
+    core_request_config *req_cfg =
+      (core_request_config *)ap_get_module_config(r->request_config,
+        &core_module);

     if (strcmp(r->handler,CGI_MAGIC_TYPE) &&
strcmp(r->handler,"cgi-script"))
         return DECLINED;
@@ -1110,7 +1113,7 @@
     /* Transfer any put/post args, CERN style...
      * Note that we already ignore SIGPIPE in the core server.
      */
-    bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+    bb = req_cfg->bb;
     seen_eos = 0;
     child_stopped_reading = 0;
     if (conf->logname) {
@@ -1121,12 +1124,14 @@
         apr_bucket *bucket;
         apr_status_t rv;

-        rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
-                            APR_BLOCK_READ, HUGE_STRING_LEN);
+ if (APR_BRIGADE_EMPTY(bb)) {
+   rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
+         APR_BLOCK_READ, HUGE_STRING_LEN);

-        if (rv != APR_SUCCESS) {
+   if (rv != APR_SUCCESS) {
             return rv;
-        }
+   }
+ }

         APR_BRIGADE_FOREACH(bucket, bb) {
             const char *data;
@@ -1173,7 +1178,6 @@
                 child_stopped_reading = 1;
             }
         }
-        apr_brigade_cleanup(bb);
     }
     while (!seen_eos);

Index: modules/http/http_protocol.c
===================================================================
RCS file: /home/cvspublic/httpd-2.0/modules/http/http_protocol.c,v
retrieving revision 1.434
diff -u -r1.434 http_protocol.c
--- modules/http/http_protocol.c 8 Jun 2002 04:36:05 -0000 1.434
+++ modules/http/http_protocol.c 10 Jun 2002 20:41:40 -0000
@@ -1867,7 +1867,10 @@
  */
 AP_DECLARE(int) ap_discard_request_body(request_rec *r)
 {
-    apr_bucket_brigade *bb;
+    core_request_config *req_cfg =
+      (core_request_config *)ap_get_module_config(r->request_config,
+        &core_module);
+    apr_bucket_brigade *bb = req_cfg->bb;
     int rv, seen_eos;

     /* Sometimes we'll get in a state where the input handling has
@@ -1881,15 +1884,15 @@
         return OK;
     }

-    bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
     seen_eos = 0;
     do {
         apr_bucket *bucket;

-        rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
-                            APR_BLOCK_READ, HUGE_STRING_LEN);
+ if (APR_BRIGADE_EMPTY(bb)) {
+   rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
+         APR_BLOCK_READ, HUGE_STRING_LEN);

-        if (rv != APR_SUCCESS) {
+   if (rv != APR_SUCCESS) {
             /* FIXME: If we ever have a mapping from filters (apr_status_t)
              * to HTTP error codes, this would be a good place for them.
              *
@@ -1898,12 +1901,13 @@
              * Otherwise, we should assume we have a bad request.
              */
             if (rv == AP_FILTER_ERROR) {
-                return rv;
+       return rv;
             }
             else {
-                return HTTP_BAD_REQUEST;
+       return HTTP_BAD_REQUEST;
             }
-        }
+   }
+ }

         APR_BRIGADE_FOREACH(bucket, bb) {
             const char *data;
Index: modules/ssl/mod_ssl.h
===================================================================
RCS file: /home/cvspublic/httpd-2.0/modules/ssl/mod_ssl.h,v
retrieving revision 1.119
diff -u -r1.119 mod_ssl.h
--- modules/ssl/mod_ssl.h 4 Jun 2002 07:12:26 -0000 1.119
+++ modules/ssl/mod_ssl.h 10 Jun 2002 20:41:40 -0000
@@ -84,6 +84,7 @@
 /* Apache headers */
 #include "httpd.h"
 #include "http_config.h"
+#define  CORE_PRIVATE
 #include "http_core.h"
 #include "http_log.h"
 #include "http_main.h"
Index: modules/ssl/ssl_engine_kernel.c
===================================================================
RCS file: /home/cvspublic/httpd-2.0/modules/ssl/ssl_engine_kernel.c,v
retrieving revision 1.72
diff -u -r1.72 ssl_engine_kernel.c
--- modules/ssl/ssl_engine_kernel.c 4 Jun 2002 07:12:26 -0000 1.72
+++ modules/ssl/ssl_engine_kernel.c 10 Jun 2002 20:41:41 -0000
@@ -613,72 +613,48 @@
     }
 #endif /* HAVE_SSL_SET_CERT_STORE */

-    /*
-     * 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.
-     *
-     * !! BUT ALL THIS IS STILL NOT RE-IMPLEMENTED FOR APACHE 2.0 !!
-     */
     if (renegotiate && (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!");
+      ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
+     "SSL: POST and renegotiate.");

-        return HTTP_METHOD_NOT_ALLOWED;
+      /* First, get ready to read the body. */
+      ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK);
+
+      if (ap_should_client_block(r)) {
+ /* Yup, there's a body here.  Let's get it. */
+
+ core_request_config *req_cfg =
+   (core_request_config *)ap_get_module_config(r->request_config,
+            &core_module);
+ apr_bucket_brigade *bb = req_cfg->bb;
+        apr_bucket_brigade *btmp = apr_brigade_create(r->pool,
+  r->connection->bucket_alloc);
+ apr_bucket *b;
+ char data[HUGE_STRING_LEN];
+ void *ptr;
+ int len_read;
+
+ /* Read all of the data and save it. */
+ while ((len_read = ap_get_client_block(r, data, HUGE_STRING_LEN)) > 0) {
+   ptr = malloc(len_read);
+   memcpy(ptr, data, len_read);
+   b = apr_bucket_heap_create(ptr, len_read, free,
+         r->connection->bucket_alloc);
+
+   APR_BRIGADE_INSERT_TAIL(btmp, b);
+
+ }
+
+ APR_BRIGADE_INSERT_TAIL(btmp, apr_bucket_eos_create(
+        r->connection->bucket_alloc));
+
+ /* Reset the data for ap_get_client_block() and
+    ap_should_client_block(). */
+ apr_brigade_destroy(bb);
+ req_cfg->bb = btmp;
+ r->read_length = 0;
+
+      }
     }

     /*




Mime
View raw message