[mod_openssl] close connection on client initiated renegotiation
parent
85b620eb3c
commit
7a9ae91566
|
@ -34,7 +34,7 @@
|
|||
* openssl.setenv "client";
|
||||
*
|
||||
* Author:
|
||||
* Copyright (c) 2009 Stefan Bühler, Joe Presbrey
|
||||
* Copyright (c) 2009-2011 Stefan Bühler, Joe Presbrey
|
||||
*/
|
||||
|
||||
#include <lighttpd/base.h>
|
||||
|
@ -57,6 +57,9 @@ struct openssl_connection_ctx {
|
|||
|
||||
int con_events;
|
||||
liJob con_handle_events_job;
|
||||
|
||||
unsigned int initial_handshaked_finished;
|
||||
unsigned int client_initiated_renegotiation;
|
||||
};
|
||||
|
||||
struct openssl_context {
|
||||
|
@ -107,9 +110,12 @@ static gboolean openssl_con_new(liConnection *con) {
|
|||
con->srv_sock_data = NULL;
|
||||
|
||||
conctx->ssl = SSL_new(ctx->ssl_ctx);
|
||||
SSL_set_app_data(conctx->ssl, conctx);
|
||||
conctx->con = con;
|
||||
li_job_init(&conctx->con_handle_events_job, openssl_con_handle_events_cb);
|
||||
conctx->con_events = 0;
|
||||
conctx->initial_handshaked_finished = 0;
|
||||
conctx->client_initiated_renegotiation = 0;
|
||||
|
||||
if (NULL == conctx->ssl) {
|
||||
ERROR(srv, "SSL_new: %s", ERR_error_string(ERR_get_error(), NULL));
|
||||
|
@ -169,6 +175,109 @@ static void openssl_update_events(liConnection *con, int events) {
|
|||
conctx->con_events = events;
|
||||
}
|
||||
|
||||
static void openssl_info_callback(const SSL *ssl, int where, int ret) {
|
||||
UNUSED(ret);
|
||||
|
||||
if (0 != (where & SSL_CB_HANDSHAKE_START)) {
|
||||
openssl_connection_ctx *conctx = SSL_get_app_data(ssl);
|
||||
if (conctx->initial_handshaked_finished) {
|
||||
conctx->client_initiated_renegotiation = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static liNetworkStatus openssl_handle_error(liConnection *con, openssl_connection_ctx *conctx, const char *sslfunc, off_t len, int r) {
|
||||
int oerrno = errno, err;
|
||||
gboolean was_fatal;
|
||||
|
||||
err = SSL_get_error(conctx->ssl, r);
|
||||
|
||||
switch (err) {
|
||||
case SSL_ERROR_WANT_READ:
|
||||
li_ev_io_add_events(con->wrk->loop, &con->sock_watcher, EV_READ);
|
||||
/* ignore requirement that we should pass the same buffer again */
|
||||
return (len > 0) ? LI_NETWORK_STATUS_SUCCESS : LI_NETWORK_STATUS_WAIT_FOR_EVENT;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
li_ev_io_add_events(con->wrk->loop, &con->sock_watcher, EV_WRITE);
|
||||
/* ignore requirement that we should pass the same buffer again */
|
||||
return (len > 0) ? LI_NETWORK_STATUS_SUCCESS : LI_NETWORK_STATUS_WAIT_FOR_EVENT;
|
||||
case SSL_ERROR_SYSCALL:
|
||||
/**
|
||||
* man SSL_get_error()
|
||||
*
|
||||
* SSL_ERROR_SYSCALL
|
||||
* Some I/O error occurred. The OpenSSL error queue may contain more
|
||||
* information on the error. If the error queue is empty (i.e.
|
||||
* ERR_get_error() returns 0), ret can be used to find out more about
|
||||
* the error: If ret == 0, an EOF was observed that violates the
|
||||
* protocol. If ret == -1, the underlying BIO reported an I/O error
|
||||
* (for socket I/O on Unix systems, consult errno for details).
|
||||
*
|
||||
*/
|
||||
while (0 != (err = ERR_get_error())) {
|
||||
VR_ERROR(con->mainvr, "%s(%i): %s", sslfunc,
|
||||
con->sock_watcher.fd,
|
||||
ERR_error_string(err, NULL));
|
||||
}
|
||||
|
||||
switch (oerrno) {
|
||||
case EPIPE:
|
||||
case ECONNRESET:
|
||||
return LI_NETWORK_STATUS_CONNECTION_CLOSE;
|
||||
}
|
||||
|
||||
if (0 != r || oerrno != 0) {
|
||||
VR_ERROR(con->mainvr, "%s(%i) returned %i: %s", sslfunc,
|
||||
con->sock_watcher.fd,
|
||||
r,
|
||||
g_strerror(oerrno));
|
||||
return LI_NETWORK_STATUS_FATAL_ERROR;
|
||||
} else {
|
||||
return LI_NETWORK_STATUS_CONNECTION_CLOSE;
|
||||
}
|
||||
|
||||
break;
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
/* clean shutdown on the remote side */
|
||||
return LI_NETWORK_STATUS_CONNECTION_CLOSE;
|
||||
default:
|
||||
was_fatal = FALSE;
|
||||
|
||||
while((err = ERR_get_error())) {
|
||||
switch (ERR_GET_REASON(err)) {
|
||||
case SSL_R_SSL_HANDSHAKE_FAILURE:
|
||||
case SSL_R_TLSV1_ALERT_UNKNOWN_CA:
|
||||
case SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN:
|
||||
case SSL_R_SSLV3_ALERT_BAD_CERTIFICATE:
|
||||
case SSL_R_NO_SHARED_CIPHER:
|
||||
case SSL_R_UNKNOWN_PROTOCOL:
|
||||
/* TODO: if (!con->conf.log_ssl_noise) */ continue;
|
||||
break;
|
||||
default:
|
||||
was_fatal = TRUE;
|
||||
break;
|
||||
}
|
||||
/* get all errors from the error-queue */
|
||||
VR_ERROR(con->mainvr, "%s(%i): %s", sslfunc,
|
||||
con->sock_watcher.fd,
|
||||
ERR_error_string(err, NULL));
|
||||
}
|
||||
if (!was_fatal) return LI_NETWORK_STATUS_CONNECTION_CLOSE;
|
||||
return LI_NETWORK_STATUS_FATAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
static liNetworkStatus openssl_do_handshake(liConnection *con, openssl_connection_ctx *conctx) {
|
||||
int r = SSL_do_handshake(conctx->ssl);
|
||||
if (1 == r) {
|
||||
conctx->initial_handshaked_finished = 1;
|
||||
conctx->ssl->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS;
|
||||
return LI_NETWORK_STATUS_SUCCESS;
|
||||
} else {
|
||||
return openssl_handle_error(con, conctx, "SSL_do_handshake", 0, r);
|
||||
}
|
||||
}
|
||||
|
||||
static liNetworkStatus openssl_con_write(liConnection *con, goffset write_max) {
|
||||
const ssize_t blocksize = 16*1024; /* 16k */
|
||||
char *block_data;
|
||||
|
@ -178,6 +287,11 @@ static liNetworkStatus openssl_con_write(liConnection *con, goffset write_max) {
|
|||
liChunkQueue *cq = con->raw_out;
|
||||
openssl_connection_ctx *conctx = con->srv_sock_data;
|
||||
|
||||
if (!conctx->initial_handshaked_finished) {
|
||||
liNetworkStatus res = openssl_do_handshake(con, conctx);
|
||||
if (res != LI_NETWORK_STATUS_SUCCESS) return res;
|
||||
}
|
||||
|
||||
do {
|
||||
if (0 == cq->length)
|
||||
return LI_NETWORK_STATUS_SUCCESS;
|
||||
|
@ -202,57 +316,13 @@ static liNetworkStatus openssl_con_write(liConnection *con, goffset write_max) {
|
|||
*/
|
||||
|
||||
ERR_clear_error();
|
||||
if ((r = SSL_write(conctx->ssl, block_data, block_len)) <= 0) {
|
||||
unsigned long err;
|
||||
|
||||
switch (SSL_get_error(conctx->ssl, r)) {
|
||||
case SSL_ERROR_WANT_READ:
|
||||
li_ev_io_add_events(con->wrk->loop, &con->sock_watcher, EV_READ);
|
||||
return LI_NETWORK_STATUS_WAIT_FOR_EVENT;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
li_ev_io_add_events(con->wrk->loop, &con->sock_watcher, EV_WRITE);
|
||||
return LI_NETWORK_STATUS_WAIT_FOR_EVENT;
|
||||
case SSL_ERROR_SYSCALL:
|
||||
/* perhaps we have error waiting in our error-queue */
|
||||
if (0 != (err = ERR_get_error())) {
|
||||
do {
|
||||
VR_ERROR(con->mainvr, "SSL_write(%i): %s",
|
||||
con->sock_watcher.fd,
|
||||
ERR_error_string(err, NULL));
|
||||
} while (0 != (err = ERR_get_error()));
|
||||
} else if (r == -1) {
|
||||
/* no, but we have errno */
|
||||
switch(errno) {
|
||||
case EPIPE:
|
||||
case ECONNRESET:
|
||||
return LI_NETWORK_STATUS_CONNECTION_CLOSE;
|
||||
default:
|
||||
VR_ERROR(con->mainvr, "SSL_write(%i): %s",
|
||||
con->sock_watcher.fd,
|
||||
g_strerror(errno));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* neither error-queue nor errno ? */
|
||||
VR_ERROR(con->mainvr, "SSL_write(%i): %s",
|
||||
con->sock_watcher.fd,
|
||||
"Unexpected eof");
|
||||
return LI_NETWORK_STATUS_CONNECTION_CLOSE;
|
||||
}
|
||||
|
||||
return LI_NETWORK_STATUS_FATAL_ERROR;
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
/* clean shutdown on the remote side */
|
||||
return LI_NETWORK_STATUS_CONNECTION_CLOSE;
|
||||
default:
|
||||
while (0 != (err = ERR_get_error())) {
|
||||
VR_ERROR(con->mainvr, "SSL_write(%i): %s",
|
||||
con->sock_watcher.fd,
|
||||
ERR_error_string(err, NULL));
|
||||
}
|
||||
|
||||
return LI_NETWORK_STATUS_FATAL_ERROR;
|
||||
}
|
||||
r = SSL_write(conctx->ssl, block_data, block_len);
|
||||
if (conctx->client_initiated_renegotiation) {
|
||||
VR_ERROR(con->mainvr, "%s", "SSL: client initiated renegotitation, closing connection");
|
||||
return LI_NETWORK_STATUS_FATAL_ERROR;
|
||||
}
|
||||
if (r <= 0) {
|
||||
return openssl_handle_error(con, conctx, "SSL_write", 0, r);
|
||||
}
|
||||
|
||||
li_chunkqueue_skip(cq, r);
|
||||
|
@ -274,6 +344,11 @@ static liNetworkStatus openssl_con_read(liConnection *con) {
|
|||
ssize_t r;
|
||||
off_t len = 0;
|
||||
|
||||
if (!conctx->initial_handshaked_finished) {
|
||||
liNetworkStatus res = openssl_do_handshake(con, conctx);
|
||||
if (res != LI_NETWORK_STATUS_SUCCESS) return res;
|
||||
}
|
||||
|
||||
if (cq->limit && cq->limit->limit > 0) {
|
||||
if (max_read > cq->limit->limit - cq->limit->current) {
|
||||
max_read = cq->limit->limit - cq->limit->current;
|
||||
|
@ -315,84 +390,12 @@ static liNetworkStatus openssl_con_read(liConnection *con) {
|
|||
assert(con->raw_in_buffer == buf);
|
||||
|
||||
r = SSL_read(conctx->ssl, buf->addr + buf->used, buf->alloc_size - buf->used);
|
||||
if (r < 0) {
|
||||
int oerrno = errno, err;
|
||||
gboolean was_fatal;
|
||||
|
||||
err = SSL_get_error(conctx->ssl, r);
|
||||
|
||||
switch (err) {
|
||||
case SSL_ERROR_WANT_READ:
|
||||
li_ev_io_add_events(con->wrk->loop, &con->sock_watcher, EV_READ);
|
||||
/* ignore requirement that we should pass the same buffer again */
|
||||
return (len > 0) ? LI_NETWORK_STATUS_SUCCESS : LI_NETWORK_STATUS_WAIT_FOR_EVENT;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
li_ev_io_add_events(con->wrk->loop, &con->sock_watcher, EV_WRITE);
|
||||
/* ignore requirement that we should pass the same buffer again */
|
||||
return (len > 0) ? LI_NETWORK_STATUS_SUCCESS : LI_NETWORK_STATUS_WAIT_FOR_EVENT;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (err) {
|
||||
case SSL_ERROR_SYSCALL:
|
||||
/**
|
||||
* man SSL_get_error()
|
||||
*
|
||||
* SSL_ERROR_SYSCALL
|
||||
* Some I/O error occurred. The OpenSSL error queue may contain more
|
||||
* information on the error. If the error queue is empty (i.e.
|
||||
* ERR_get_error() returns 0), ret can be used to find out more about
|
||||
* the error: If ret == 0, an EOF was observed that violates the
|
||||
* protocol. If ret == -1, the underlying BIO reported an I/O error
|
||||
* (for socket I/O on Unix systems, consult errno for details).
|
||||
*
|
||||
*/
|
||||
while (0 != (err = ERR_get_error())) {
|
||||
VR_ERROR(con->mainvr, "SSL_read(%i): %s",
|
||||
con->sock_watcher.fd,
|
||||
ERR_error_string(err, NULL));
|
||||
}
|
||||
|
||||
switch (oerrno) {
|
||||
case EPIPE:
|
||||
case ECONNRESET:
|
||||
return LI_NETWORK_STATUS_CONNECTION_CLOSE;
|
||||
}
|
||||
|
||||
VR_ERROR(con->mainvr, "SSL_read(%i): %s",
|
||||
con->sock_watcher.fd,
|
||||
g_strerror(oerrno));
|
||||
|
||||
break;
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
/* clean shutdown on the remote side */
|
||||
return LI_NETWORK_STATUS_CONNECTION_CLOSE;
|
||||
default:
|
||||
was_fatal = FALSE;
|
||||
|
||||
while((err = ERR_get_error())) {
|
||||
switch (ERR_GET_REASON(err)) {
|
||||
case SSL_R_SSL_HANDSHAKE_FAILURE:
|
||||
case SSL_R_TLSV1_ALERT_UNKNOWN_CA:
|
||||
case SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN:
|
||||
case SSL_R_SSLV3_ALERT_BAD_CERTIFICATE:
|
||||
/* TODO: if (!con->conf.log_ssl_noise) */ continue;
|
||||
break;
|
||||
default:
|
||||
was_fatal = TRUE;
|
||||
break;
|
||||
}
|
||||
/* get all errors from the error-queue */
|
||||
VR_ERROR(con->mainvr, "SSL_read(%i): %s",
|
||||
con->sock_watcher.fd,
|
||||
ERR_error_string(err, NULL));
|
||||
}
|
||||
if (!was_fatal) return LI_NETWORK_STATUS_CONNECTION_CLOSE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (conctx->client_initiated_renegotiation) {
|
||||
VR_ERROR(con->mainvr, "%s", "SSL: client initiated renegotitation, closing connection");
|
||||
return LI_NETWORK_STATUS_FATAL_ERROR;
|
||||
}
|
||||
if (r < 0) {
|
||||
return openssl_handle_error(con, conctx, "SSL_read", len, r);
|
||||
} else if (r == 0) {
|
||||
return LI_NETWORK_STATUS_CONNECTION_CLOSE;
|
||||
}
|
||||
|
@ -773,6 +776,8 @@ static gboolean openssl_setup(liServer *srv, liPlugin* p, liValue *val, gpointer
|
|||
goto error_free_socket;
|
||||
}
|
||||
|
||||
SSL_CTX_set_info_callback(ctx->ssl_ctx, openssl_info_callback);
|
||||
|
||||
if (!SSL_CTX_set_options(ctx->ssl_ctx, options)) {
|
||||
ERROR(srv, "SSL_CTX_set_options(%lx): %s", options, ERR_error_string(ERR_get_error(), NULL));
|
||||
goto error_free_socket;
|
||||
|
|
Loading…
Reference in New Issue