From d3cf43347bfae6f05799b9e39c707886150686b9 Mon Sep 17 00:00:00 2001 From: "Ralf S. Engelschall" Date: Sat, 5 May 2001 16:23:00 +0000 Subject: [PATCH] Axe out SSL_CONSERVATIVE stuff which for Apache 1.3 did I/O data pre-sucking on POST requests and I/O re-injection in case of SSL renegotiations. This all either cannot be solved any longer or at least has to be implemented totally different through I/O layering/filtering. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@89017 13f79535-47bb-0310-9956-ffa450edef68 --- modules/ssl/README | 3 + modules/ssl/mod_ssl.h | 3 - modules/ssl/ssl_engine_io.c | 223 -------------------------------- modules/ssl/ssl_engine_kernel.c | 67 ++++++++-- 4 files changed, 61 insertions(+), 235 deletions(-) diff --git a/modules/ssl/README b/modules/ssl/README index 8f33707612..f2bcfefc28 100644 --- a/modules/ssl/README +++ b/modules/ssl/README @@ -145,6 +145,9 @@ o The complete EAPI-based SSL_VENDOR stuff was removed. o The complete EAPI-based SSL_COMPAT stuff was removed. o The variable MOD_SSL is no longer provided automatically + o The complete SSL_CONSERVATIVE stuff was removed, i.e., + SSL renegotiations in combination with POST request are not supported + unless the problem is solved again, but this time through layered I/O. MAJOR CHANGES diff --git a/modules/ssl/mod_ssl.h b/modules/ssl/mod_ssl.h index 823df609d2..59c40b776c 100644 --- a/modules/ssl/mod_ssl.h +++ b/modules/ssl/mod_ssl.h @@ -778,9 +778,6 @@ char *ssl_var_lookup(pool *, server_rec *, conn_rec *, request_rec *, cha void ssl_io_register(void); void ssl_io_unregister(void); long ssl_io_data_cb(BIO *, int, const char *, int, long, long); -#ifndef SSL_CONSERVATIVE -void ssl_io_suck(request_rec *, SSL *); -#endif /* PRNG */ int ssl_rand_seed(server_rec *, pool *, ssl_rsctx_t, char *); diff --git a/modules/ssl/ssl_engine_io.c b/modules/ssl/ssl_engine_io.c index 3b03343372..e56901415e 100644 --- a/modules/ssl/ssl_engine_io.c +++ b/modules/ssl/ssl_engine_io.c @@ -64,229 +64,6 @@ #if 0 /* XXX */ -/* _________________________________________________________________ -** -** I/O Request Body Sucking and Re-Injection -** _________________________________________________________________ -*/ - -#ifndef SSL_CONSERVATIVE - -/* - * 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. - */ - -struct ssl_io_suck_st { - BOOL active; - char *bufptr; - int buflen; - char *pendptr; - int pendlen; -}; - -/* prepare request_rec structure for input sucking */ -static void ssl_io_suck_start(request_rec *r) -{ - struct ssl_io_suck_st *ss; - - ss = ap_ctx_get(r->ctx, "ssl::io::suck"); - if (ss == NULL) { - ss = ap_palloc(r->pool, sizeof(struct ssl_io_suck_st)); - ap_ctx_set(r->ctx, "ssl::io::suck", ss); - ss->buflen = 8192; - ss->bufptr = ap_palloc(r->pool, ss->buflen); - } - ss->pendptr = ss->bufptr; - ss->pendlen = 0; - ss->active = FALSE; - return; -} - -/* record a sucked input chunk */ -static void ssl_io_suck_record(request_rec *r, char *buf, int len) -{ - struct ssl_io_suck_st *ss; - - if ((ss = ap_ctx_get(r->ctx, "ssl::io::suck")) == NULL) - return; - if (((ss->bufptr + ss->buflen) - (ss->pendptr + ss->pendlen)) < len) { - /* "expand" buffer: actually we cannot really expand the buffer - here, because Apache's pool system doesn't support expanding chunks - of memory. Instead we have to either reuse processed data or - allocate a new chunk of memory in advance if we really need more - memory. */ - int newlen; - char *newptr; - - if (( (ss->pendptr - ss->bufptr) - + ((ss->bufptr + ss->buflen) - (ss->pendptr + ss->pendlen)) ) >= len) { - /* make memory available by reusing already processed data */ - memmove(ss->bufptr, ss->pendptr, ss->pendlen); - ss->pendptr = ss->bufptr; - } - else { - /* too bad, we have to allocate a new larger buffer */ - newlen = (ss->buflen * 2) + len; - newptr = ap_palloc(r->pool, newlen); - ss->bufptr = newptr; - ss->buflen = newlen; - memcpy(ss->bufptr, ss->pendptr, ss->pendlen); - ss->pendptr = ss->bufptr; - } - } - memcpy(ss->pendptr+ss->pendlen, buf, len); - ss->pendlen += len; - return; -} - -/* finish request_rec after input sucking */ -static void ssl_io_suck_end(request_rec *r) -{ - struct ssl_io_suck_st *ss; - - if ((ss = ap_ctx_get(r->ctx, "ssl::io::suck")) == NULL) - return; - ss->active = TRUE; - r->read_body = REQUEST_NO_BODY; - r->read_length = 0; - r->read_chunked = 0; - r->remaining = 0; - ap_bsetflag(r->connection->client, B_CHUNK, 0); - return; -} - -void ssl_io_suck(request_rec *r, SSL *ssl) -{ - int rc; - int len; - char *buf; - int buflen; - char c; - int sucked; - - if ((rc = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK)) == OK) { - if (ap_should_client_block(r)) { - - /* read client request block through Apache API */ - buflen = HUGE_STRING_LEN; - buf = ap_palloc(r->pool, buflen); - ap_hard_timeout("SSL I/O request body pre-sucking", r); - sucked = 0; - ssl_io_suck_start(r); - while ((len = ap_get_client_block(r, buf, buflen)) > 0) { - ssl_io_suck_record(r, buf, len); - sucked += len; - } - ssl_io_suck_end(r); - ap_kill_timeout(r); - - /* suck trailing data (usually CR LF) which - is still in the Apache BUFF layer */ - while (ap_bpeekc(r->connection->client) != EOF) { - c = ap_bgetc(r->connection->client); - ssl_io_suck_record(r, &c, 1); - sucked++; - } - - ssl_log(r->server, SSL_LOG_TRACE, - "I/O: sucked %d bytes of input data from SSL/TLS I/O layer " - "for delayed injection into Apache I/O layer", sucked); - } - } - return; -} - -/* the SSL_read replacement routine which knows about the suck buffer */ -static int ssl_io_suck_read(SSL *ssl, char *buf, int len) -{ - ap_ctx *actx; - struct ssl_io_suck_st *ss; - request_rec *r = NULL; - int rv; - - actx = (ap_ctx *)SSL_get_app_data2(ssl); - if (actx != NULL) - r = (request_rec *)ap_ctx_get(actx, "ssl::request_rec"); - - rv = -1; - if (r != NULL) { - ss = ap_ctx_get(r->ctx, "ssl::io::suck"); - if (ss != NULL) { - if (ss->active && ss->pendlen > 0) { - /* ok, there is pre-sucked data */ - len = (ss->pendlen > len ? len : ss->pendlen); - memcpy(buf, ss->pendptr, len); - ss->pendptr += len; - ss->pendlen -= len; - ssl_log(r->server, SSL_LOG_TRACE, - "I/O: injecting %d bytes of pre-sucked data " - "into Apache I/O layer", len); - rv = len; - } - } - } - if (rv == -1) - rv = SSL_read(ssl, buf, len); - return rv; -} - -/* override SSL_read in the following code... */ -#define SSL_read ssl_io_suck_read - -#endif /* !SSL_CONSERVATIVE */ - /* _________________________________________________________________ ** ** I/O Hooks diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c index d0c9e1d7b2..5566c9b64f 100644 --- a/modules/ssl/ssl_engine_kernel.c +++ b/modules/ssl/ssl_engine_kernel.c @@ -857,19 +857,71 @@ int ssl_hook_Access(request_rec *r) } #endif /* SSL_EXPERIMENTAL_PERDIRCA */ -#ifdef SSL_CONSERVATIVE /* - * SSL renegotiations in conjunction with HTTP - * requests using the POST method are not supported. + * 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) { ssl_log(r->server, SSL_LOG_ERROR, "SSL Re-negotiation in conjunction with POST method not supported!"); - ssl_log(r->server, SSL_LOG_INFO, - "You have to compile without -DSSL_CONSERVATIVE to enabled support for this."); return METHOD_NOT_ALLOWED; } -#endif /* SSL_CONSERVATIVE */ /* * now do the renegotiation if anything was actually reconfigured @@ -922,9 +974,6 @@ int ssl_hook_Access(request_rec *r) SSL_set_session_id_context(ssl, (unsigned char *)&(r->main), sizeof(r->main)); else SSL_set_session_id_context(ssl, (unsigned char *)&r, sizeof(r)); -#ifndef SSL_CONSERVATIVE - ssl_io_suck(r, ssl); -#endif SSL_renegotiate(ssl); SSL_do_handshake(ssl); if (SSL_get_state(ssl) != SSL_ST_OK) { -- 2.50.1