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_VENDOR stuff was removed.
|
||||||
o The complete EAPI-based SSL_COMPAT 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 <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
|
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_register(void);
|
||||||
void ssl_io_unregister(void);
|
void ssl_io_unregister(void);
|
||||||
long ssl_io_data_cb(BIO *, int, const char *, int, long, long);
|
long ssl_io_data_cb(BIO *, int, const char *, int, long, long);
|
||||||
#ifndef SSL_CONSERVATIVE
|
|
||||||
void ssl_io_suck(request_rec *, SSL *);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* PRNG */
|
/* PRNG */
|
||||||
int ssl_rand_seed(server_rec *, pool *, ssl_rsctx_t, char *);
|
int ssl_rand_seed(server_rec *, pool *, ssl_rsctx_t, char *);
|
||||||
|
@@ -64,229 +64,6 @@
|
|||||||
|
|
||||||
#if 0 /* XXX */
|
#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
|
** I/O Hooks
|
||||||
|
@@ -857,19 +857,71 @@ int ssl_hook_Access(request_rec *r)
|
|||||||
}
|
}
|
||||||
#endif /* SSL_EXPERIMENTAL_PERDIRCA */
|
#endif /* SSL_EXPERIMENTAL_PERDIRCA */
|
||||||
|
|
||||||
#ifdef SSL_CONSERVATIVE
|
|
||||||
/*
|
/*
|
||||||
* SSL renegotiations in conjunction with HTTP
|
* SSL renegotiations in conjunction with HTTP
|
||||||
* requests using the POST method are not supported.
|
* 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) {
|
if (renegotiate && r->method_number == M_POST) {
|
||||||
ssl_log(r->server, SSL_LOG_ERROR,
|
ssl_log(r->server, SSL_LOG_ERROR,
|
||||||
"SSL Re-negotiation in conjunction with POST method not supported!");
|
"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;
|
return METHOD_NOT_ALLOWED;
|
||||||
}
|
}
|
||||||
#endif /* SSL_CONSERVATIVE */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* now do the renegotiation if anything was actually reconfigured
|
* 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));
|
SSL_set_session_id_context(ssl, (unsigned char *)&(r->main), sizeof(r->main));
|
||||||
else
|
else
|
||||||
SSL_set_session_id_context(ssl, (unsigned char *)&r, sizeof(r));
|
SSL_set_session_id_context(ssl, (unsigned char *)&r, sizeof(r));
|
||||||
#ifndef SSL_CONSERVATIVE
|
|
||||||
ssl_io_suck(r, ssl);
|
|
||||||
#endif
|
|
||||||
SSL_renegotiate(ssl);
|
SSL_renegotiate(ssl);
|
||||||
SSL_do_handshake(ssl);
|
SSL_do_handshake(ssl);
|
||||||
if (SSL_get_state(ssl) != SSL_ST_OK) {
|
if (SSL_get_state(ssl) != SSL_ST_OK) {
|
||||||
|
Reference in New Issue
Block a user