mirror of
https://github.com/apache/httpd.git
synced 2025-08-08 15:02:10 +03:00
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
This commit is contained in:
@@ -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 <IfDefine> 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
|
||||
|
||||
|
@@ -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 *);
|
||||
|
@@ -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
|
||||
|
@@ -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.
|
||||
*
|
||||
* 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) {
|
||||
|
Reference in New Issue
Block a user