Commit b5ea369c authored by William A. Rowe Jr's avatar William A. Rowe Jr
Browse files

  Merge the last of the 'filtering' functions into ssl_engine_io.c, merge
  ssl_abort into what was ssl_hook_CloseConnection, clean out a bunch of
  now-static or private headers from mod_ssl.h, and final fix a very small
  but potent segfault if ->pssl is destroyed within our read loop.


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk/modules/ssl@97411 13f79535-47bb-0310-9956-ffa450edef68
parent ef12be94
Loading
Loading
Loading
Loading
+0 −183
Changes for mod_ssl.c: 0 added lines, 183 removed lines.
Original line number Diff line number Diff line
@@ -368,189 +368,6 @@ static int ssl_hook_pre_connection(conn_rec *c, void *csd)
    return APR_SUCCESS;
}

static apr_status_t ssl_abort(SSLFilterRec *filter, conn_rec *c)
{
    SSLConnRec *sslconn = myConnConfig(c);
    /*
     * try to gracefully shutdown the connection:
     * - send an own shutdown message (be gracefully)
     * - don't wait for peer's shutdown message (deadloop)
     * - kick away the SSL stuff immediately
     * - block the socket, so Apache cannot operate any more
     */

    SSL_set_shutdown(filter->pssl, SSL_RECEIVED_SHUTDOWN);
    SSL_smart_shutdown(filter->pssl);
    SSL_free(filter->pssl);

    filter->pssl = NULL; /* so filters know we've been shutdown */
    sslconn->ssl = NULL;
    c->aborted = 1;

    return APR_EGENERAL;
}

/*
 * The hook is NOT registered with ap_hook_process_connection. Instead, it is
 * called manually from the churn () before it tries to read any data.
 * There is some problem if I accept conn_rec *. Still investigating..
 * Adv. if conn_rec * can be accepted is we can hook this function using the
 * ap_hook_process_connection hook.
 */
int ssl_hook_process_connection(SSLFilterRec *filter)
{
    conn_rec *c         = (conn_rec *)SSL_get_app_data(filter->pssl);
    SSLConnRec *sslconn = myConnConfig(c);
    SSLSrvConfigRec *sc = mySrvConfig(c->base_server);
    X509 *cert;
    int n, err;
    long verify_result;

    if (!SSL_is_init_finished(filter->pssl)) {
        if (sslconn->is_proxy) {
            if ((n = SSL_connect(filter->pssl)) <= 0) {
                ap_log_error(APLOG_MARK, APLOG_ERR, 0,
                             c->base_server,
                             "SSL Proxy connect failed");
                ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, c->base_server);
                return ssl_abort(filter, c);
            }

            return APR_SUCCESS;
        }

        if ((n = SSL_accept(filter->pssl)) <= 0) {
            err = SSL_get_error(filter->pssl, n);

            if (err == SSL_ERROR_ZERO_RETURN) {
                /*
                 * The case where the connection was closed before any data
                 * was transferred. That's not a real error and can occur
                 * sporadically with some clients.
                 */
                ap_log_error(APLOG_MARK, APLOG_INFO, 0,
                             c->base_server,
                             "SSL handshake stopped: connection was closed");
            }
            else if (err == SSL_ERROR_WANT_READ) {
                /*
                 * This is in addition to what was present earlier. It is 
                 * borrowed from openssl_state_machine.c [mod_tls].
                 * TBD.
                 */
                return SSL_ERROR_WANT_READ;
            }
            else if (ERR_GET_REASON(ERR_peek_error()) == SSL_R_HTTP_REQUEST) {
                /*
                 * The case where OpenSSL has recognized a HTTP request:
                 * This means the client speaks plain HTTP on our HTTPS port.
                 * ssl_io_filter_error will disable the ssl filters when it
                 * sees this status code.
                 */
                return HTTP_BAD_REQUEST;
            }
            else if ((SSL_get_error(filter->pssl, n) == SSL_ERROR_SYSCALL) &&
                     (errno != EINTR))
            {
                if (errno > 0) {
                    ap_log_error(APLOG_MARK, APLOG_ERR, 0,
                                 c->base_server,
                                 "SSL handshake interrupted by system "
                                 "[Hint: Stop button pressed in browser?!]");
                    ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, c->base_server);
                }
                else {
                    ap_log_error(APLOG_MARK, APLOG_ERR, 0, 
                                 c->base_server,
                                 "Spurious SSL handshake interrupt [Hint: "
                                 "Usually just one of those OpenSSL "
                                 "confusions!?]");
                    ssl_log_ssl_error(APLOG_MARK, APLOG_INFO, c->base_server);
                }
            }
            else {
                /*
                 * Ok, anything else is a fatal error
                 */
                ap_log_error(APLOG_MARK, APLOG_ERR, 0, 
                             c->base_server,
                             "SSL handshake failed (server %s, client %s)",
                             ssl_util_vhostid(c->pool, c->base_server),
                             c->remote_ip ? c->remote_ip : "unknown");
                ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, c->base_server);
            }

            return ssl_abort(filter, c);
        }

        /*
         * Check for failed client authentication
         */
        verify_result = SSL_get_verify_result(filter->pssl);

        if ((verify_result != X509_V_OK) ||
            sslconn->verify_error)
        {
            if (ssl_verify_error_is_optional(verify_result) &&
                (sc->server->auth.verify_mode == SSL_CVERIFY_OPTIONAL_NO_CA))
            {
                /* leaving this log message as an error for the moment,
                 * according to the mod_ssl docs:
                 * "level optional_no_ca is actually against the idea
                 *  of authentication (but can be used to establish 
                 * SSL test pages, etc.)"
                 * optional_no_ca doesn't appear to work as advertised
                 * in 1.x
                 */
                ap_log_error(APLOG_MARK, APLOG_ERR, 0,
                             c->base_server,
                             "SSL client authentication failed, "
                             "accepting certificate based on "
                             "\"SSLVerifyClient optional_no_ca\" "
                             "configuration");
                ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, c->base_server);
            }
            else {
                const char *error = sslconn->verify_error ?
                    sslconn->verify_error :
                    X509_verify_cert_error_string(verify_result);

                ap_log_error(APLOG_MARK, APLOG_ERR, 0,
                             c->base_server,
                             "SSL client authentication failed: %s",
                             error ? error : "unknown");
                ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, c->base_server);

                return ssl_abort(filter, c);
            }
        }

        /*
         * Remember the peer certificate's DN
         */
        if ((cert = SSL_get_peer_certificate(filter->pssl))) {
            sslconn->client_cert = cert;
            sslconn->client_dn = NULL;
            X509_free(cert);
        }

        /*
         * Make really sure that when a peer certificate
         * is required we really got one... (be paranoid)
         */
        if ((sc->server->auth.verify_mode == SSL_CVERIFY_REQUIRE) &&
            !sslconn->client_cert)
        {
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, c->base_server,
                         "No acceptable peer certificate available");

            return ssl_abort(filter, c);
        }
    }

    return APR_SUCCESS;
}

static const char *ssl_hook_http_method(const request_rec *r)
{
    SSLSrvConfigRec *sc = mySrvConfig(r->server);
+0 −13
Changes for mod_ssl.h: 0 added lines, 13 removed lines.
Original line number Diff line number Diff line
@@ -389,14 +389,6 @@ typedef struct {
 * (i.e. the global configuration for each httpd process)
 */

typedef struct {
    SSL                *pssl;
    BIO                *pbioRead;
    BIO                *pbioWrite;
    ap_filter_t        *pInputFilter;
    ap_filter_t        *pOutputFilter;
} SSLFilterRec;

typedef enum {
    SSL_SHUTDOWN_TYPE_UNSET,
    SSL_SHUTDOWN_TYPE_STANDARD,
@@ -590,17 +582,12 @@ void ssl_init_Child(apr_pool_t *, server_rec *);
apr_status_t ssl_init_ModuleKill(void *data);

/*  Apache API hooks  */
void         ssl_hook_NewConnection(conn_rec *);
void         ssl_hook_TimeoutConnection(int);
int          ssl_hook_process_connection(SSLFilterRec *pRec);
apr_status_t ssl_hook_CloseConnection(SSLFilterRec *);
int          ssl_hook_Translate(request_rec *);
int          ssl_hook_Auth(request_rec *);
int          ssl_hook_UserCheck(request_rec *);
int          ssl_hook_Access(request_rec *);
int          ssl_hook_Fixup(request_rec *);
int          ssl_hook_ReadReq(request_rec *);
int          ssl_hook_Handler(request_rec *);

/*  OpenSSL callbacks */
RSA         *ssl_callback_TmpRSA(SSL *, int, int);
+458 −178

File changed.

Preview size limit exceeded, changes collapsed.

+0 −99
Changes for ssl_engine_kernel.c: 0 added lines, 99 removed lines.
Original line number Diff line number Diff line
@@ -63,105 +63,6 @@
                                            -- Unknown                */
#include "mod_ssl.h"

/*
 *  Close the SSL part of the socket connection
 *  (called immediately _before_ the socket is closed)
 */
/* XXX: perhaps ssl_abort() should call us or vice-versa
 * lot of the same happening in both places
 */
apr_status_t ssl_hook_CloseConnection(SSLFilterRec *filter)
{
    SSL *ssl = filter->pssl;
    const char *type = "";
    conn_rec *conn;
    SSLConnRec *sslconn;

    if (!ssl) {
        return APR_SUCCESS;
    }

    conn = (conn_rec *)SSL_get_app_data(ssl);
    sslconn = myConnConfig(conn);

    /*
     * Now close the SSL layer of the connection. We've to take
     * the TLSv1 standard into account here:
     *
     * | 7.2.1. Closure alerts
     * |
     * | The client and the server must share knowledge that the connection is
     * | ending in order to avoid a truncation attack. Either party may
     * | initiate the exchange of closing messages.
     * |
     * | close_notify
     * |     This message notifies the recipient that the sender will not send
     * |     any more messages on this connection. The session becomes
     * |     unresumable if any connection is terminated without proper
     * |     close_notify messages with level equal to warning.
     * |
     * | Either party may initiate a close by sending a close_notify alert.
     * | Any data received after a closure alert is ignored.
     * |
     * | Each party is required to send a close_notify alert before closing
     * | the write side of the connection. It is required that the other party
     * | respond with a close_notify alert of its own and close down the
     * | connection immediately, discarding any pending writes. It is not
     * | required for the initiator of the close to wait for the responding
     * | close_notify alert before closing the read side of the connection.
     *
     * This means we've to send a close notify message, but haven't to wait
     * for the close notify of the client. Actually we cannot wait for the
     * close notify of the client because some clients (including Netscape
     * 4.x) don't send one, so we would hang.
     */

    /*
     * exchange close notify messages, but allow the user
     * to force the type of handshake via SetEnvIf directive
     */
    switch (sslconn->shutdown_type) {
      case SSL_SHUTDOWN_TYPE_UNSET:
      case SSL_SHUTDOWN_TYPE_STANDARD:
        /* send close notify, but don't wait for clients close notify
           (standard compliant and safe, so it's the DEFAULT!) */
        SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
        type = "standard";
        break;
      case SSL_SHUTDOWN_TYPE_UNCLEAN:
        /* perform no close notify handshake at all
           (violates the SSL/TLS standard!) */
        SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
        type = "unclean";
        break;
      case SSL_SHUTDOWN_TYPE_ACCURATE:
        /* send close notify and wait for clients close notify
           (standard compliant, but usually causes connection hangs) */
        SSL_set_shutdown(ssl, 0);
        type = "accurate";
        break;
    }

    SSL_smart_shutdown(ssl);

    /* and finally log the fact that we've closed the connection */
    if (conn->base_server->loglevel >= APLOG_INFO) {
        ap_log_error(APLOG_MARK, APLOG_INFO, 0, conn->base_server,
                     "Connection to child %ld closed with %s shutdown"
                     "(server %s, client %s)",
                     conn->id, type,
                     ssl_util_vhostid(conn->pool, conn->base_server),
                     conn->remote_ip ? conn->remote_ip : "unknown");
    }

    /* deallocate the SSL connection */
    SSL_free(ssl);
    sslconn->ssl = NULL;
    filter->pssl = NULL; /* so filters know we've been shutdown */

    return APR_SUCCESS;
}

/*
 *  Post Read Request Handler
 */