[mod_openssl] close connection on client initiated renegotiation

personal/stbuehler/wip
Stefan Bühler 11 years ago
parent 85b620eb3c
commit 7a9ae91566
  1. 263
      src/modules/mod_openssl.c

@ -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…
Cancel
Save