Commit 70c61cd9 authored by Ralf S. Engelschall's avatar Ralf S. Engelschall
Browse files

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
parent 8c46d543
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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 

+0 −3
Original line number Diff line number Diff line
@@ -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 *);
+0 −223
Original line number Diff line number Diff line
@@ -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
+58 −9
Original line number Diff line number Diff line
@@ -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) {