mirror of
https://github.com/apache/httpd.git
synced 2025-08-07 04:02:58 +03:00
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@97411 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
@@ -368,189 +368,6 @@ static int ssl_hook_pre_connection(conn_rec *c, void *csd)
|
|||||||
return APR_SUCCESS;
|
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)
|
static const char *ssl_hook_http_method(const request_rec *r)
|
||||||
{
|
{
|
||||||
SSLSrvConfigRec *sc = mySrvConfig(r->server);
|
SSLSrvConfigRec *sc = mySrvConfig(r->server);
|
||||||
|
@@ -389,14 +389,6 @@ typedef struct {
|
|||||||
* (i.e. the global configuration for each httpd process)
|
* (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 {
|
typedef enum {
|
||||||
SSL_SHUTDOWN_TYPE_UNSET,
|
SSL_SHUTDOWN_TYPE_UNSET,
|
||||||
SSL_SHUTDOWN_TYPE_STANDARD,
|
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);
|
apr_status_t ssl_init_ModuleKill(void *data);
|
||||||
|
|
||||||
/* Apache API hooks */
|
/* 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_Translate(request_rec *);
|
||||||
int ssl_hook_Auth(request_rec *);
|
int ssl_hook_Auth(request_rec *);
|
||||||
int ssl_hook_UserCheck(request_rec *);
|
int ssl_hook_UserCheck(request_rec *);
|
||||||
int ssl_hook_Access(request_rec *);
|
int ssl_hook_Access(request_rec *);
|
||||||
int ssl_hook_Fixup(request_rec *);
|
int ssl_hook_Fixup(request_rec *);
|
||||||
int ssl_hook_ReadReq(request_rec *);
|
int ssl_hook_ReadReq(request_rec *);
|
||||||
int ssl_hook_Handler(request_rec *);
|
|
||||||
|
|
||||||
/* OpenSSL callbacks */
|
/* OpenSSL callbacks */
|
||||||
RSA *ssl_callback_TmpRSA(SSL *, int, int);
|
RSA *ssl_callback_TmpRSA(SSL *, int, int);
|
||||||
|
@@ -73,15 +73,15 @@
|
|||||||
* remember what is in this file. So, first, a quick overview.
|
* remember what is in this file. So, first, a quick overview.
|
||||||
*
|
*
|
||||||
* In this file, you will find:
|
* In this file, you will find:
|
||||||
* - ssl_io_filter_Input (Apache input filter)
|
* - ssl_io_filter_input (Apache input filter)
|
||||||
* - ssl_io_filter_Output (Apache output filter)
|
* - ssl_io_filter_output (Apache output filter)
|
||||||
*
|
*
|
||||||
* - bio_filter_in_* (OpenSSL input filter)
|
* - bio_filter_in_* (OpenSSL input filter)
|
||||||
* - bio_filter_out_* (OpenSSL output filter)
|
* - bio_filter_out_* (OpenSSL output filter)
|
||||||
*
|
*
|
||||||
* The input chain is roughly:
|
* 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
|
* ...->bio_filter_in_read->ap_get_brigade/next-httpd-filter
|
||||||
*
|
*
|
||||||
* In mortal terminology, we do the following:
|
* In mortal terminology, we do the following:
|
||||||
@@ -106,7 +106,7 @@
|
|||||||
* ssl_io_input_read may be able to fulfill reads without invoking
|
* ssl_io_input_read may be able to fulfill reads without invoking
|
||||||
* SSL_read().
|
* 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.
|
* are shared as bio_filter_in_ctx_t.
|
||||||
*
|
*
|
||||||
* Note that the filter is by choice limited to reading at most
|
* Note that the filter is by choice limited to reading at most
|
||||||
@@ -129,7 +129,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
typedef struct {
|
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;
|
conn_rec *c;
|
||||||
apr_bucket_brigade *bb;
|
apr_bucket_brigade *bb;
|
||||||
apr_size_t length;
|
apr_size_t length;
|
||||||
@@ -138,7 +146,7 @@ typedef struct {
|
|||||||
apr_status_t rc;
|
apr_status_t rc;
|
||||||
} bio_filter_out_ctx_t;
|
} 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)
|
conn_rec *c)
|
||||||
{
|
{
|
||||||
bio_filter_out_ctx_t *outctx = apr_palloc(c->pool, sizeof(*outctx));
|
bio_filter_out_ctx_t *outctx = apr_palloc(c->pool, sizeof(*outctx));
|
||||||
@@ -348,7 +356,7 @@ typedef struct {
|
|||||||
char_buffer_t cbuf;
|
char_buffer_t cbuf;
|
||||||
apr_pool_t *pool;
|
apr_pool_t *pool;
|
||||||
char buffer[AP_IOBUFSIZE];
|
char buffer[AP_IOBUFSIZE];
|
||||||
SSLFilterRec *filter_ctx;
|
ssl_filter_ctx_t *filter_ctx;
|
||||||
} bio_filter_in_ctx_t;
|
} bio_filter_in_ctx_t;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -568,151 +576,6 @@ static BIO_METHOD bio_filter_in_method = {
|
|||||||
#endif
|
#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,
|
static apr_status_t ssl_io_input_read(bio_filter_in_ctx_t *inctx,
|
||||||
char *buf,
|
char *buf,
|
||||||
@@ -753,6 +616,10 @@ static apr_status_t ssl_io_input_read(bio_filter_in_ctx_t *inctx,
|
|||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
|
||||||
|
if (!inctx->filter_ctx->pssl) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/* SSL_read may not read because we haven't taken enough data
|
/* SSL_read may not read because we haven't taken enough data
|
||||||
* from the stack. This is where we want to consider all of
|
* from the stack. This is where we want to consider all of
|
||||||
* the blocking and SPECULATIVE semantics
|
* the blocking and SPECULATIVE semantics
|
||||||
@@ -887,6 +754,74 @@ static apr_status_t ssl_io_input_getline(bio_filter_in_ctx_t *inctx,
|
|||||||
return APR_SUCCESS;
|
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
|
/* 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
|
* 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.
|
* gets the request back to the Apache core so that a response can be sent.
|
||||||
@@ -943,7 +878,293 @@ static apr_status_t ssl_io_filter_error(ap_filter_t *f,
|
|||||||
return APR_SUCCESS;
|
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,
|
apr_bucket_brigade *bb,
|
||||||
ap_input_mode_t mode,
|
ap_input_mode_t mode,
|
||||||
apr_read_type_e block,
|
apr_read_type_e block,
|
||||||
@@ -978,12 +1199,12 @@ static apr_status_t ssl_io_filter_Input(ap_filter_t *f,
|
|||||||
inctx->mode = mode;
|
inctx->mode = mode;
|
||||||
inctx->block = block;
|
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_hook_process_connection but would still need to call it for
|
||||||
* AP_MODE_INIT for protocols that may upgrade the connection
|
* AP_MODE_INIT for protocols that may upgrade the connection
|
||||||
* rather than have SSLEngine On configured.
|
* 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) {
|
if (status != APR_SUCCESS) {
|
||||||
return ssl_io_filter_error(f, bb, status);
|
return ssl_io_filter_error(f, bb, status);
|
||||||
@@ -1027,7 +1248,84 @@ static apr_status_t ssl_io_filter_Input(ap_filter_t *f,
|
|||||||
return APR_SUCCESS;
|
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)
|
SSL *ssl)
|
||||||
{
|
{
|
||||||
bio_filter_in_ctx_t *inctx;
|
bio_filter_in_ctx_t *inctx;
|
||||||
@@ -1050,29 +1348,11 @@ static void ssl_io_input_add_filter(SSLFilterRec *filter_ctx, conn_rec *c,
|
|||||||
inctx->pool = c->pool;
|
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)
|
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->pOutputFilter = ap_add_output_filter(ssl_io_filter,
|
||||||
filter_ctx, NULL, c);
|
filter_ctx, NULL, c);
|
||||||
@@ -1098,8 +1378,8 @@ void ssl_io_filter_init(conn_rec *c, SSL *ssl)
|
|||||||
|
|
||||||
void ssl_io_filter_register(apr_pool_t *p)
|
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_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_output_filter (ssl_io_filter, ssl_io_filter_output, NULL, AP_FTYPE_CONNECTION + 5);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -63,105 +63,6 @@
|
|||||||
-- Unknown */
|
-- Unknown */
|
||||||
#include "mod_ssl.h"
|
#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
|
* Post Read Request Handler
|
||||||
*/
|
*/
|
||||||
|
Reference in New Issue
Block a user