httpd-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From wr...@apache.org
Subject cvs commit: httpd-2.0/modules/ssl mod_ssl.c mod_ssl.h ssl_engine_io.c ssl_engine_kernel.c
Date Tue, 05 Nov 2002 20:47:01 GMT
wrowe       2002/11/05 12:47:01

  Modified:    modules/ssl mod_ssl.c mod_ssl.h ssl_engine_io.c
                        ssl_engine_kernel.c
  Log:
    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.
  
  Revision  Changes    Path
  1.74      +0 -183    httpd-2.0/modules/ssl/mod_ssl.c
  
  Index: mod_ssl.c
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/modules/ssl/mod_ssl.c,v
  retrieving revision 1.73
  retrieving revision 1.74
  diff -u -r1.73 -r1.74
  --- mod_ssl.c	29 Oct 2002 03:52:22 -0000	1.73
  +++ mod_ssl.c	5 Nov 2002 20:47:01 -0000	1.74
  @@ -368,189 +368,6 @@
       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);
  
  
  
  1.122     +0 -13     httpd-2.0/modules/ssl/mod_ssl.h
  
  Index: mod_ssl.h
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/modules/ssl/mod_ssl.h,v
  retrieving revision 1.121
  retrieving revision 1.122
  diff -u -r1.121 -r1.122
  --- mod_ssl.h	14 Oct 2002 04:15:58 -0000	1.121
  +++ mod_ssl.h	5 Nov 2002 20:47:01 -0000	1.122
  @@ -389,14 +389,6 @@
    * (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 @@
   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);
  
  
  
  1.96      +458 -178  httpd-2.0/modules/ssl/ssl_engine_io.c
  
  Index: ssl_engine_io.c
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/modules/ssl/ssl_engine_io.c,v
  retrieving revision 1.95
  retrieving revision 1.96
  diff -u -r1.95 -r1.96
  --- ssl_engine_io.c	5 Nov 2002 06:38:41 -0000	1.95
  +++ ssl_engine_io.c	5 Nov 2002 20:47:01 -0000	1.96
  @@ -73,15 +73,15 @@
    * remember what is in this file.  So, first, a quick overview.
    *
    * In this file, you will find:
  - * - ssl_io_filter_Input    (Apache input filter)
  - * - ssl_io_filter_Output   (Apache output filter)
  + * - ssl_io_filter_input    (Apache input filter)
  + * - ssl_io_filter_output   (Apache output filter)
    *
    * - bio_filter_in_*        (OpenSSL input filter)
    * - bio_filter_out_*       (OpenSSL output filter)
    *
    * The input chain is roughly:
    *
  - * ssl_io_filter_Input->ssl_io_input_read->SSL_read->...
  + * ssl_io_filter_input->ssl_io_input_read->SSL_read->...
    * ...->bio_filter_in_read->ap_get_brigade/next-httpd-filter
    *
    * In mortal terminology, we do the following:
  @@ -106,7 +106,7 @@
    * ssl_io_input_read may be able to fulfill reads without invoking
    * SSL_read().
    *
  - * Note that the filter context of ssl_io_filter_Input and bio_filter_in_*
  + * Note that the filter context of ssl_io_filter_input and bio_filter_in_*
    * are shared as bio_filter_in_ctx_t.
    *
    * Note that the filter is by choice limited to reading at most
  @@ -129,7 +129,15 @@
    */
   
   typedef struct {
  -    SSLFilterRec *filter_ctx;
  +    SSL                *pssl;
  +    BIO                *pbioRead;
  +    BIO                *pbioWrite;
  +    ap_filter_t        *pInputFilter;
  +    ap_filter_t        *pOutputFilter;
  +} ssl_filter_ctx_t;
  +
  +typedef struct {
  +    ssl_filter_ctx_t *filter_ctx;
       conn_rec *c;
       apr_bucket_brigade *bb;
       apr_size_t length;
  @@ -138,7 +146,7 @@
       apr_status_t rc;
   } bio_filter_out_ctx_t;
   
  -static bio_filter_out_ctx_t *bio_filter_out_ctx_new(SSLFilterRec *filter_ctx,
  +static bio_filter_out_ctx_t *bio_filter_out_ctx_new(ssl_filter_ctx_t *filter_ctx,
                                                       conn_rec *c)
   {
       bio_filter_out_ctx_t *outctx = apr_palloc(c->pool, sizeof(*outctx));
  @@ -348,7 +356,7 @@
       char_buffer_t cbuf;
       apr_pool_t *pool;
       char buffer[AP_IOBUFSIZE];
  -    SSLFilterRec *filter_ctx;
  +    ssl_filter_ctx_t *filter_ctx;
   } bio_filter_in_ctx_t;
   
   /*
  @@ -568,151 +576,6 @@
   #endif
   };
   
  -static const char ssl_io_filter[] = "SSL/TLS Filter";
  -
  -static apr_status_t ssl_filter_write(ap_filter_t *f,
  -                                     const char *data,
  -                                     apr_size_t len)
  -{
  -    SSLFilterRec *filter_ctx = f->ctx;
  -    bio_filter_out_ctx_t *outctx = 
  -           (bio_filter_out_ctx_t *)(filter_ctx->pbioWrite->ptr);
  -    int res;
  -
  -    /* write SSL */
  -    if (filter_ctx->pssl == NULL) {
  -        return APR_EGENERAL;
  -    }
  -
  -    res = SSL_write(filter_ctx->pssl, (unsigned char *)data, len);
  -
  -    if (res < 0) {
  -        int ssl_err = SSL_get_error(filter_ctx->pssl, res);
  -
  -        if (ssl_err == SSL_ERROR_WANT_WRITE) {
  -            /*
  -             * If OpenSSL wants to write more, and we were nonblocking,
  -             * report as an EAGAIN.  Otherwise loop, pushing more
  -             * data at the network filter.
  -             *
  -             * (This is usually the case when the client forces an SSL
  -             * renegotation which is handled implicitly by OpenSSL.)
  -             */
  -            outctx->rc = APR_EAGAIN;
  -        }
  -        else if (ssl_err == SSL_ERROR_SYSCALL) {
  -            conn_rec *c = (conn_rec*)SSL_get_app_data(outctx->filter_ctx->pssl);
  -            ap_log_error(APLOG_MARK, APLOG_ERR, outctx->rc, c->base_server,
  -                        "SSL filter out error writing data");
  -        }
  -        else /* if (ssl_err == SSL_ERROR_SSL) */ {
  -            /*
  -             * Log SSL errors
  -             */
  -            conn_rec *c = (conn_rec *)SSL_get_app_data(filter_ctx->pssl);
  -            ap_log_error(APLOG_MARK, APLOG_ERR, outctx->rc, c->base_server,
  -                    "SSL library out error writing data");
  -            ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, c->base_server);
  -
  -        }
  -        if (outctx->rc == APR_SUCCESS) {
  -            outctx->rc = APR_EGENERAL;
  -        }
  -    }
  -    else if ((apr_size_t)res != len) {
  -        conn_rec *c = f->c;
  -        char *reason = "reason unknown";
  -
  -        /* XXX: probably a better way to determine this */
  -        if (SSL_total_renegotiations(filter_ctx->pssl)) {
  -            reason = "likely due to failed renegotiation";
  -        }
  -
  -        ap_log_error(APLOG_MARK, APLOG_ERR, outctx->rc, c->base_server,
  -                     "failed to write %d of %d bytes (%s)",
  -                     len - (apr_size_t)res, len, reason);
  -
  -        outctx->rc = APR_EGENERAL;
  -    }
  -    return outctx->rc;
  -}
  -
  -static apr_status_t ssl_io_filter_Output(ap_filter_t *f,
  -                                         apr_bucket_brigade *bb)
  -{
  -    apr_status_t status = APR_SUCCESS;
  -    SSLFilterRec *filter_ctx = f->ctx;
  -
  -    if (f->c->aborted) {
  -        apr_brigade_cleanup(bb);
  -        return APR_ECONNABORTED;
  -    }
  -
  -    if (!filter_ctx->pssl) {
  -        /* ssl_abort() has been called */
  -        return ap_pass_brigade(f->next, bb);
  -    }
  -
  -    if ((status = ssl_hook_process_connection(filter_ctx)) != APR_SUCCESS) {
  -        return status;
  -    }
  -
  -    while (!APR_BRIGADE_EMPTY(bb)) {
  -        apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
  -
  -        /* If it is a flush or EOS, we need to pass this down. 
  -         * These types do not require translation by OpenSSL.  
  -         */
  -        if (APR_BUCKET_IS_EOS(bucket) || APR_BUCKET_IS_FLUSH(bucket)) {
  -            if (bio_filter_out_flush(filter_ctx->pbioWrite) < 0) {
  -                bio_filter_out_ctx_t *outctx = 
  -                       (bio_filter_out_ctx_t *)(filter_ctx->pbioWrite->ptr);
  -                status = outctx->rc;
  -                break;
  -            }
  -
  -            if (APR_BUCKET_IS_EOS(bucket)) {
  -                /*
  -                 * By definition, nothing can come after EOS.
  -                 * which also means we can pass the rest of this brigade
  -                 * without creating a new one since it only contains the
  -                 * EOS bucket.
  -                 */
  -
  -                if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) {
  -                    return status;
  -                }
  -                break;
  -            }
  -            else {
  -                /* bio_filter_out_flush() already passed down a flush bucket
  -                 * if there was any data to be flushed.
  -                 */
  -                apr_bucket_delete(bucket);
  -            }
  -        }
  -        else {
  -            /* read filter */
  -            const char *data;
  -            apr_size_t len;
  -            
  -            status = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
  -
  -            if (!APR_STATUS_IS_EOF(status) && (status != APR_SUCCESS)) {
  -                break;
  -            }
  -
  -            status = ssl_filter_write(f, data, len);
  -            apr_bucket_delete(bucket);
  -
  -            if (status != APR_SUCCESS) {
  -                break;
  -            }
  -        }
  -    }
  -
  -    return status;
  -}
   
   static apr_status_t ssl_io_input_read(bio_filter_in_ctx_t *inctx,
                                         char *buf,
  @@ -753,6 +616,10 @@
   
       while (1) {
   
  +        if (!inctx->filter_ctx->pssl) {
  +            break;
  +        }
  +
           /* SSL_read may not read because we haven't taken enough data
            * from the stack.  This is where we want to consider all of
            * the blocking and SPECULATIVE semantics
  @@ -887,6 +754,74 @@
       return APR_SUCCESS;
   }
   
  +
  +static apr_status_t ssl_filter_write(ap_filter_t *f,
  +                                     const char *data,
  +                                     apr_size_t len)
  +{
  +    ssl_filter_ctx_t *filter_ctx = f->ctx;
  +    bio_filter_out_ctx_t *outctx = 
  +           (bio_filter_out_ctx_t *)(filter_ctx->pbioWrite->ptr);
  +    int res;
  +
  +    /* write SSL */
  +    if (filter_ctx->pssl == NULL) {
  +        return APR_EGENERAL;
  +    }
  +
  +    res = SSL_write(filter_ctx->pssl, (unsigned char *)data, len);
  +
  +    if (res < 0) {
  +        int ssl_err = SSL_get_error(filter_ctx->pssl, res);
  +
  +        if (ssl_err == SSL_ERROR_WANT_WRITE) {
  +            /*
  +             * If OpenSSL wants to write more, and we were nonblocking,
  +             * report as an EAGAIN.  Otherwise loop, pushing more
  +             * data at the network filter.
  +             *
  +             * (This is usually the case when the client forces an SSL
  +             * renegotation which is handled implicitly by OpenSSL.)
  +             */
  +            outctx->rc = APR_EAGAIN;
  +        }
  +        else if (ssl_err == SSL_ERROR_SYSCALL) {
  +            conn_rec *c = (conn_rec*)SSL_get_app_data(outctx->filter_ctx->pssl);
  +            ap_log_error(APLOG_MARK, APLOG_ERR, outctx->rc, c->base_server,
  +                        "SSL filter out error writing data");
  +        }
  +        else /* if (ssl_err == SSL_ERROR_SSL) */ {
  +            /*
  +             * Log SSL errors
  +             */
  +            conn_rec *c = (conn_rec *)SSL_get_app_data(filter_ctx->pssl);
  +            ap_log_error(APLOG_MARK, APLOG_ERR, outctx->rc, c->base_server,
  +                    "SSL library out error writing data");
  +            ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, c->base_server);
  +
  +        }
  +        if (outctx->rc == APR_SUCCESS) {
  +            outctx->rc = APR_EGENERAL;
  +        }
  +    }
  +    else if ((apr_size_t)res != len) {
  +        conn_rec *c = f->c;
  +        char *reason = "reason unknown";
  +
  +        /* XXX: probably a better way to determine this */
  +        if (SSL_total_renegotiations(filter_ctx->pssl)) {
  +            reason = "likely due to failed renegotiation";
  +        }
  +
  +        ap_log_error(APLOG_MARK, APLOG_ERR, outctx->rc, c->base_server,
  +                     "failed to write %d of %d bytes (%s)",
  +                     len - (apr_size_t)res, len, reason);
  +
  +        outctx->rc = APR_EGENERAL;
  +    }
  +    return outctx->rc;
  +}
  +
   /* Just use a simple request.  Any request will work for this, because
    * we use a flag in the conn_rec->conn_vector now.  The fake request just
    * gets the request back to the Apache core so that a response can be sent.
  @@ -943,7 +878,293 @@
       return APR_SUCCESS;
   }
   
  -static apr_status_t ssl_io_filter_Input(ap_filter_t *f,
  +static const char ssl_io_filter[] = "SSL/TLS Filter";
  +
  +/*
  + *  Close the SSL part of the socket connection
  + *  (called immediately _before_ the socket is closed)
  + *  or called with 
  + */
  +static apr_status_t ssl_filter_io_shutdown(ssl_filter_ctx_t *filter_ctx,
  +                                           conn_rec *c,
  +                                           int abortive)
  +{
  +    SSL *ssl = filter_ctx->pssl;
  +    const char *type = "";
  +    SSLConnRec *sslconn = myConnConfig(c);
  +    int shutdown_type;
  +
  +    if (!ssl) {
  +        return APR_SUCCESS;
  +    }
  +
  +    /*
  +     * 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
  +     */
  +    if (abortive) {
  +        shutdown_type = SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN;
  +        type = "abortive";
  +    }
  +    else 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!) */
  +        shutdown_type = SSL_RECEIVED_SHUTDOWN;
  +        type = "standard";
  +        break;
  +      case SSL_SHUTDOWN_TYPE_UNCLEAN:
  +        /* perform no close notify handshake at all
  +           (violates the SSL/TLS standard!) */
  +        shutdown_type = 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) */
  +        shutdown_type = 0;
  +        type = "accurate";
  +        break;
  +    }
  +
  +    SSL_set_shutdown(ssl, shutdown_type);
  +    SSL_smart_shutdown(ssl);
  +
  +    /* and finally log the fact that we've closed the connection */
  +    if (c->base_server->loglevel >= APLOG_INFO) {
  +        ap_log_error(APLOG_MARK, APLOG_INFO, 0, c->base_server,
  +                     "Connection to child %ld closed with %s shutdown"
  +                     "(server %s, client %s)",
  +                     c->id, type,
  +                     ssl_util_vhostid(c->pool, c->base_server),
  +                     c->remote_ip ? c->remote_ip : "unknown");
  +    }
  +
  +    /* deallocate the SSL connection */
  +    SSL_free(ssl);
  +    sslconn->ssl = NULL;
  +    filter_ctx->pssl = NULL; /* so filters know we've been shutdown */
  +
  +    return APR_SUCCESS;
  +}
  +
  +static apr_status_t ssl_io_filter_cleanup(void *data)
  +{
  +    apr_status_t ret;
  +    ssl_filter_ctx_t *filter_ctx = (ssl_filter_ctx_t *)data;
  +    conn_rec *c;
  +
  +    if (!filter_ctx->pssl) {
  +        /* already been shutdown */
  +        return APR_SUCCESS;
  +    }
  +
  +    c = (conn_rec *)SSL_get_app_data(filter_ctx->pssl);
  +    if ((ret = ssl_filter_io_shutdown(filter_ctx, c, 0)) != APR_SUCCESS) {
  +        ap_log_error(APLOG_MARK, APLOG_ERR, ret, NULL,
  +                     "SSL filter error shutting down I/O");
  +    }
  +
  +    return ret;
  +}
  +
  +/*
  + * 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.
  + */
  +static int ssl_io_filter_connect(ssl_filter_ctx_t *filter_ctx)
  +{
  +    conn_rec *c         = (conn_rec *)SSL_get_app_data(filter_ctx->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_ctx->pssl)) {
  +        return APR_SUCCESS;
  +    }
  +
  +    if (sslconn->is_proxy) {
  +        if ((n = SSL_connect(filter_ctx->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_filter_io_shutdown(filter_ctx, c, 1);
  +        }
  +
  +        return APR_SUCCESS;
  +    }
  +
  +    if ((n = SSL_accept(filter_ctx->pssl)) <= 0) {
  +        err = SSL_get_error(filter_ctx->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_ctx->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_filter_io_shutdown(filter_ctx, c, 1);
  +    }
  +
  +    /*
  +     * Check for failed client authentication
  +     */
  +    verify_result = SSL_get_verify_result(filter_ctx->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_filter_io_shutdown(filter_ctx, c, 1);
  +        }
  +    }
  +
  +    /*
  +     * Remember the peer certificate's DN
  +     */
  +    if ((cert = SSL_get_peer_certificate(filter_ctx->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_filter_io_shutdown(filter_ctx, c, 1);
  +    }
  +
  +    return APR_SUCCESS;
  +}
  +
  +static apr_status_t ssl_io_filter_input(ap_filter_t *f,
                                           apr_bucket_brigade *bb,
                                           ap_input_mode_t mode,
                                           apr_read_type_e block,
  @@ -978,12 +1199,12 @@
       inctx->mode = mode;
       inctx->block = block;
   
  -    /* XXX: we could actually move ssl_hook_process_connection to an
  +    /* XXX: we could actually move ssl_io_filter_connect to an
        * ap_hook_process_connection but would still need to call it for
        * AP_MODE_INIT for protocols that may upgrade the connection
        * rather than have SSLEngine On configured.
        */
  -    status = ssl_hook_process_connection(inctx->filter_ctx);
  +    status = ssl_io_filter_connect(inctx->filter_ctx);
   
       if (status != APR_SUCCESS) {
           return ssl_io_filter_error(f, bb, status);
  @@ -1027,7 +1248,84 @@
       return APR_SUCCESS;
   }
   
  -static void ssl_io_input_add_filter(SSLFilterRec *filter_ctx, conn_rec *c,
  +static apr_status_t ssl_io_filter_output(ap_filter_t *f,
  +                                         apr_bucket_brigade *bb)
  +{
  +    apr_status_t status = APR_SUCCESS;
  +    ssl_filter_ctx_t *filter_ctx = f->ctx;
  +
  +    if (f->c->aborted) {
  +        apr_brigade_cleanup(bb);
  +        return APR_ECONNABORTED;
  +    }
  +
  +    if (!filter_ctx->pssl) {
  +        /* ssl_filter_io_shutdown was called */
  +        return ap_pass_brigade(f->next, bb);
  +    }
  +
  +    if ((status = ssl_io_filter_connect(filter_ctx)) != APR_SUCCESS) {
  +        return status;
  +    }
  +
  +    while (!APR_BRIGADE_EMPTY(bb)) {
  +        apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
  +
  +        /* If it is a flush or EOS, we need to pass this down. 
  +         * These types do not require translation by OpenSSL.  
  +         */
  +        if (APR_BUCKET_IS_EOS(bucket) || APR_BUCKET_IS_FLUSH(bucket)) {
  +            if (bio_filter_out_flush(filter_ctx->pbioWrite) < 0) {
  +                bio_filter_out_ctx_t *outctx = 
  +                       (bio_filter_out_ctx_t *)(filter_ctx->pbioWrite->ptr);
  +                status = outctx->rc;
  +                break;
  +            }
  +
  +            if (APR_BUCKET_IS_EOS(bucket)) {
  +                /*
  +                 * By definition, nothing can come after EOS.
  +                 * which also means we can pass the rest of this brigade
  +                 * without creating a new one since it only contains the
  +                 * EOS bucket.
  +                 */
  +
  +                if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) {
  +                    return status;
  +                }
  +                break;
  +            }
  +            else {
  +                /* bio_filter_out_flush() already passed down a flush bucket
  +                 * if there was any data to be flushed.
  +                 */
  +                apr_bucket_delete(bucket);
  +            }
  +        }
  +        else {
  +            /* filter output */
  +            const char *data;
  +            apr_size_t len;
  +            
  +            status = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
  +
  +            if (!APR_STATUS_IS_EOF(status) && (status != APR_SUCCESS)) {
  +                break;
  +            }
  +
  +            status = ssl_filter_write(f, data, len);
  +            apr_bucket_delete(bucket);
  +
  +            if (status != APR_SUCCESS) {
  +                break;
  +            }
  +        }
  +    }
  +
  +    return status;
  +}
  +
  +static void ssl_io_input_add_filter(ssl_filter_ctx_t *filter_ctx, conn_rec *c,
                                       SSL *ssl)
   {
       bio_filter_in_ctx_t *inctx;
  @@ -1050,29 +1348,11 @@
       inctx->pool = c->pool;
   }
   
  -static apr_status_t ssl_io_filter_cleanup (void *data)
  -{
  -    apr_status_t ret;
  -    SSLFilterRec *filter_ctx = (SSLFilterRec *)data;
  -
  -    if (!filter_ctx->pssl) {
  -        /* already been shutdown */
  -        return APR_SUCCESS;
  -    }
  -
  -    if ((ret = ssl_hook_CloseConnection(filter_ctx)) != APR_SUCCESS) {
  -        ap_log_error(APLOG_MARK, APLOG_ERR, ret, NULL,
  -                     "Error in ssl_hook_CloseConnection");
  -    }
  -
  -    return ret;
  -}
  -
   void ssl_io_filter_init(conn_rec *c, SSL *ssl)
   {
  -    SSLFilterRec *filter_ctx;
  +    ssl_filter_ctx_t *filter_ctx;
   
  -    filter_ctx = apr_palloc(c->pool, sizeof(SSLFilterRec));
  +    filter_ctx = apr_palloc(c->pool, sizeof(ssl_filter_ctx_t));
   
       filter_ctx->pOutputFilter   = ap_add_output_filter(ssl_io_filter,
                                                      filter_ctx, NULL, c);
  @@ -1098,8 +1378,8 @@
   
   void ssl_io_filter_register(apr_pool_t *p)
   {
  -    ap_register_input_filter  (ssl_io_filter, ssl_io_filter_Input,  NULL, AP_FTYPE_CONNECTION
+ 5);
  -    ap_register_output_filter (ssl_io_filter, ssl_io_filter_Output, NULL, AP_FTYPE_CONNECTION
+ 5);
  +    ap_register_input_filter  (ssl_io_filter, ssl_io_filter_input,  NULL, AP_FTYPE_CONNECTION
+ 5);
  +    ap_register_output_filter (ssl_io_filter, ssl_io_filter_output, NULL, AP_FTYPE_CONNECTION
+ 5);
       return;
   }
   
  
  
  
  1.82      +0 -99     httpd-2.0/modules/ssl/ssl_engine_kernel.c
  
  Index: ssl_engine_kernel.c
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/modules/ssl/ssl_engine_kernel.c,v
  retrieving revision 1.81
  retrieving revision 1.82
  diff -u -r1.81 -r1.82
  --- ssl_engine_kernel.c	29 Oct 2002 03:52:22 -0000	1.81
  +++ ssl_engine_kernel.c	5 Nov 2002 20:47:01 -0000	1.82
  @@ -64,105 +64,6 @@
   #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
    */
   int ssl_hook_ReadReq(request_rec *r)
  
  
  

Mime
View raw message