You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by wr...@apache.org on 2002/11/05 21:47:01 UTC
cvs commit: httpd-2.0/modules/ssl mod_ssl.c mod_ssl.h ssl_engine_io.c ssl_engine_kernel.c
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)
Re: cvs commit: httpd-2.0/modules/ssl mod_ssl.c mod_ssl.h
ssl_engine_io.c ssl_engine_kernel.c
Posted by Justin Erenkrantz <je...@apache.org>.
--On Tuesday, November 05, 2002 20:47:01 +0000 wrowe@apache.org wrote:
> 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)
Woo-hoo!
> @@ -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;
>
> /*
> @@ -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");
Can't we get the conn_rec from outctx->filter_ctx->pOutputFilter->c rather
than trying to be cute and calling SSL_get_app_data? This pattern seems to
be happening a lot in this code and this seems like a better and much
faster approach.
> + }
> + 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) {
Just as a note that might want to enable partial write support in OpenSSL.
Not sure, but it might perform better? So, we may want to remove the res
!= len check at some point. -- justin