From d095b834a9cc6df5aad8ec49bfc945c06b35af4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?okhowang=28=E7=8E=8B=E6=B2=9B=E6=96=87=29?= Date: Tue, 7 Jul 2020 17:26:46 +0800 Subject: Merge ssl implementations (openssl and mbedtls) This patch splits common part out to avoid copy-paste from the - bufferevent_openssl.c - bufferevent_mbedtls.c It uses VFS/bufferevent-like approach, i.e. structure of callbacks. --- CMakeLists.txt | 4 +- Makefile.am | 6 +- bufferevent-internal.h | 8 +- bufferevent_mbedtls.c | 1349 ++++++++---------------------------------------- bufferevent_openssl.c | 1202 ++++-------------------------------------- bufferevent_ssl.c | 1093 +++++++++++++++++++++++++++++++++++++++ ssl-compat.h | 102 ++++ 7 files changed, 1534 insertions(+), 2230 deletions(-) create mode 100644 bufferevent_ssl.c create mode 100644 ssl-compat.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 90052916..89f0ca32 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -864,7 +864,7 @@ if (NOT EVENT__DISABLE_OPENSSL) include_directories(${OPENSSL_INCLUDE_DIR}) - list(APPEND SRC_OPENSSL bufferevent_openssl.c) + list(APPEND SRC_OPENSSL bufferevent_openssl.c bufferevent_ssl.c) list(APPEND HDR_PUBLIC include/event2/bufferevent_ssl.h) list(APPEND LIB_APPS ${OPENSSL_LIBRARIES}) endif() @@ -879,7 +879,7 @@ if (NOT EVENT__DISABLE_MBEDTLS) include_directories(${MBEDTLS_INCLUDE_DIR}) - list(APPEND SRC_MBEDTLS bufferevent_mbedtls.c) + list(APPEND SRC_MBEDTLS bufferevent_mbedtls.c bufferevent_ssl.c) list(APPEND HDR_PUBLIC include/event2/bufferevent_ssl.h) list(APPEND LIB_APPS ${MBEDTLS_LIBRARIES}) endif() diff --git a/Makefile.am b/Makefile.am index a004e898..73e76b0b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -102,6 +102,7 @@ LIBEVENT_PKGCONFIG=libevent.pc libevent_core.pc libevent_extra.pc PLATFORM_DEPENDENT_SRC = \ arc4random.c \ epoll_sub.c \ + bufferevent_ssl.c \ test/regress_ssl.c CMAKE_FILES = \ @@ -293,14 +294,14 @@ libevent_extra_la_LIBADD = $(MAYBE_CORE) $(SYS_LIBS) libevent_extra_la_LDFLAGS = $(GENERIC_LDFLAGS) if OPENSSL -libevent_openssl_la_SOURCES = bufferevent_openssl.c +libevent_openssl_la_SOURCES = bufferevent_openssl.c bufferevent_ssl.c libevent_openssl_la_LIBADD = $(MAYBE_CORE) $(OPENSSL_LIBS) libevent_openssl_la_LDFLAGS = $(GENERIC_LDFLAGS) libevent_openssl_la_CPPFLAGS = $(AM_CPPFLAGS) $(OPENSSL_INCS) endif if MBEDTLS -libevent_mbedtls_la_SOURCES = bufferevent_mbedtls.c +libevent_mbedtls_la_SOURCES = bufferevent_mbedtls.c bufferevent_ssl.c libevent_mbedtls_la_LIBADD = $(MAYBE_CORE) $(MBEDTLS_LIBS) libevent_mbedtls_la_LDFLAGS = $(GENERIC_LDFLAGS) libevent_mbedtls_la_CPPFLAGS = $(AM_CPPFLAGS) $(MBEDTLS_INCS) @@ -336,6 +337,7 @@ noinst_HEADERS += \ time-internal.h \ util-internal.h \ openssl-compat.h \ + ssl-compat.h \ wepoll.h EVENT1_HDRS = \ diff --git a/bufferevent-internal.h b/bufferevent-internal.h index 94a9cb3c..0404d4ff 100644 --- a/bufferevent-internal.h +++ b/bufferevent-internal.h @@ -306,11 +306,11 @@ extern const struct bufferevent_ops bufferevent_ops_pair; #define BEV_IS_FILTER(bevp) ((bevp)->be_ops == &bufferevent_ops_filter) #define BEV_IS_PAIR(bevp) ((bevp)->be_ops == &bufferevent_ops_pair) -#if defined(EVENT__HAVE_OPENSSL) -extern const struct bufferevent_ops bufferevent_ops_openssl; -#define BEV_IS_OPENSSL(bevp) ((bevp)->be_ops == &bufferevent_ops_openssl) +#if defined(EVENT__HAVE_OPENSSL) | defined(EVENT__HAVE_MBEDTLS) +extern const struct bufferevent_ops bufferevent_ops_ssl; +#define BEV_IS_SSL(bevp) ((bevp)->be_ops == &bufferevent_ops_ssl) #else -#define BEV_IS_OPENSSL(bevp) 0 +#define BEV_IS_SSL(bevp) 0 #endif #if defined(EVENT__HAVE_MBEDTLS) diff --git a/bufferevent_mbedtls.c b/bufferevent_mbedtls.c index 5dc95f47..f1422019 100644 --- a/bufferevent_mbedtls.c +++ b/bufferevent_mbedtls.c @@ -24,51 +24,116 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -// Get rid of OSX 10.7 and greater deprecation warnings. -#if defined(__APPLE__) && defined(__clang__) -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -#endif - -#include "event2/event-config.h" -#include "evconfig-private.h" - -#include - -#ifdef EVENT__HAVE_SYS_TIME_H -#include -#endif - -#include -#include -#include -#include -#ifdef EVENT__HAVE_STDARG_H -#include -#endif -#ifdef EVENT__HAVE_UNISTD_H -#include -#endif - -#ifdef _WIN32 -#include -#endif +#include +#include +#include +#include "event2/util.h" +#include "util-internal.h" +#include "event2/buffer.h" #include "event2/bufferevent.h" #include "event2/bufferevent_struct.h" #include "event2/bufferevent_ssl.h" -#include "event2/buffer.h" -#include "event2/event.h" +#include "ssl-compat.h" #include "mm-internal.h" -#include "bufferevent-internal.h" -#include "log-internal.h" -#include -#include -#include -#define SSL_ERROR_WANT_READ MBEDTLS_ERR_SSL_WANT_READ -#define SSL_ERROR_WANT_WRITE MBEDTLS_ERR_SSL_WANT_WRITE -#define SSL mbedtls_ssl_context +struct mbedtls_context { + mbedtls_ssl_context *ssl; + mbedtls_net_context net; +}; +static void * +mbedtls_context_init(void *ssl) +{ + struct mbedtls_context *ctx = mm_malloc(sizeof(*ctx)); + if (ctx) { + ctx->ssl = ssl; + ctx->net.fd = -1; + } + return ctx; +} +static void +mbedtls_context_free(void *ssl, int flags) +{ + struct mbedtls_context *ctx = ssl; + if (flags & BEV_OPT_CLOSE_ON_FREE) + mbedtls_ssl_free(ctx->ssl); + mm_free(ctx); +} +static int +mbedtls_context_renegotiate(void *ssl) +{ + struct mbedtls_context *ctx = ssl; + return mbedtls_ssl_renegotiate(ctx->ssl); +} +static int +mbedtls_context_write(void *ssl, const unsigned char *buf, size_t len) +{ + struct mbedtls_context *ctx = ssl; + return mbedtls_ssl_write(ctx->ssl, buf, len); +} +static int +mbedtls_context_read(void *ssl, unsigned char *buf, size_t len) +{ + struct mbedtls_context *ctx = ssl; + return mbedtls_ssl_read(ctx->ssl, buf, len); +} +static size_t +mbedtls_context_pending(void *ssl) +{ + struct mbedtls_context *ctx = ssl; + return mbedtls_ssl_get_bytes_avail(ctx->ssl); +} +static int +mbedtls_context_handshake(void *ssl) +{ + struct mbedtls_context *ctx = ssl; + return mbedtls_ssl_handshake(ctx->ssl); +} +static int +mbedtls_get_error(void *ssl, int ret) +{ + return ret; +} +static void +mbedtls_clear_error(void) +{ +} +static int +mbedtls_clear(void *ssl) +{ + return 1; +} +static void +mbedtls_set_ssl_noops(void *ssl) +{ +} +static int +mbedtls_is_ok(int err) +{ + return err == 0; +} +static int +mbedtls_is_want_read(int err) +{ + return err == MBEDTLS_ERR_SSL_WANT_READ; +} +static int +mbedtls_is_want_write(int err) +{ + return err == MBEDTLS_ERR_SSL_WANT_WRITE; +} + +static evutil_socket_t +be_mbedtls_get_fd(void *ssl) +{ + struct bufferevent_ssl *bev = ssl; + struct mbedtls_context *ctx = bev->ssl; + return ctx->net.fd; +} + +static int be_mbedtls_bio_set_fd( + struct bufferevent_ssl *bev_ssl, evutil_socket_t fd); #if 0 static void @@ -79,15 +144,17 @@ print_err(int val) printf("Error was %d:%s\n", val, buf); } #else -#define print_err(v) ((void)0) +static void +print_err(int val) +{ +} #endif - /* Called to extract data from the BIO. */ static int bio_bufferevent_read(void *ctx, unsigned char *out, size_t outlen) { - struct bufferevent *bufev = (struct bufferevent*)ctx; + struct bufferevent *bufev = (struct bufferevent *)ctx; int r = 0; struct evbuffer *input; @@ -111,7 +178,7 @@ bio_bufferevent_read(void *ctx, unsigned char *out, size_t outlen) static int bio_bufferevent_write(void *ctx, const unsigned char *in, size_t inlen) { - struct bufferevent *bufev = (struct bufferevent*)ctx; + struct bufferevent *bufev = (struct bufferevent *)ctx; struct evbuffer *output; size_t outlen; @@ -123,7 +190,7 @@ bio_bufferevent_write(void *ctx, const unsigned char *in, size_t inlen) /* Copy only as much data onto the output buffer as can fit under the * high-water mark. */ - if (bufev->wm_write.high && bufev->wm_write.high <= (outlen+inlen)) { + if (bufev->wm_write.high && bufev->wm_write.high <= (outlen + inlen)) { if (bufev->wm_write.high <= outlen) { /* If no data can fit, we'll need to retry later. */ return MBEDTLS_ERR_SSL_WANT_WRITE; @@ -136,239 +203,13 @@ bio_bufferevent_write(void *ctx, const unsigned char *in, size_t inlen) return inlen; } - -/* -------------------- - Now, here's the mbedTLS-based implementation of bufferevent. - - The implementation comes in only one flavors, that has the - SSL object connect to a socket directly. - -------------------- */ - -struct bio_data_counts { - unsigned long n_written; - unsigned long n_read; -}; - -struct bufferevent_mbedtls { - /* Shared fields with common bufferevent implementation code. - If we were set up with an underlying bufferevent, we use the - events here as timers only. If we have an SSL, then we use - the events as socket events. - */ - struct bufferevent_private bev; - /* An underlying bufferevent that we're directing our output to. - If it's NULL, then we're connected to an fd, not an evbuffer. */ - struct bufferevent *underlying; - /* net fd */ - mbedtls_net_context net_ctx; - /* The SSL object doing our encryption. */ - SSL *ssl; - - /* A callback that's invoked when data arrives on our outbuf so we - know to write data to the SSL. */ - struct evbuffer_cb_entry *outbuf_cb; - - /* A count of how much data the bios have read/written total. Used - for rate-limiting. */ - struct bio_data_counts counts; - - /* If this value is greater than 0, then the last SSL_write blocked, - * and we need to try it again with this many bytes. */ - ev_ssize_t last_write; - -#define NUM_ERRORS 3 - ev_uint32_t errors[NUM_ERRORS]; - - /* When we next get available space, we should say "read" instead of - "write". This can happen if there's a renegotiation during a read - operation. */ - unsigned read_blocked_on_write : 1; - /* When we next get data, we should say "write" instead of "read". */ - unsigned write_blocked_on_read : 1; - /* Treat TCP close before SSL close on SSL >= v3 as clean EOF. */ - unsigned allow_dirty_shutdown : 1; - /* XXX */ - unsigned n_errors : 2; - - /* Are we currently connecting, accepting, or doing IO? */ - unsigned state : 2; - /* If we reset fd, we sould reset state too */ - unsigned old_state : 2; -}; - -static int be_mbedtls_enable(struct bufferevent *, short); -static int be_mbedtls_disable(struct bufferevent *, short); -static void be_mbedtls_unlink(struct bufferevent *); -static void be_mbedtls_destruct(struct bufferevent *); -static int be_mbedtls_adj_timeouts(struct bufferevent *); -static int be_mbedtls_flush(struct bufferevent *bufev, - short iotype, enum bufferevent_flush_mode mode); -static int be_mbedtls_ctrl(struct bufferevent *, enum bufferevent_ctrl_op, union bufferevent_ctrl_data *); - -const struct bufferevent_ops bufferevent_ops_mbedtls = { - "mbedtls", - evutil_offsetof(struct bufferevent_mbedtls, bev.bev), - be_mbedtls_enable, - be_mbedtls_disable, - be_mbedtls_unlink, - be_mbedtls_destruct, - be_mbedtls_adj_timeouts, - be_mbedtls_flush, - be_mbedtls_ctrl, -}; - -/* Given a bufferevent, return a pointer to the bufferevent_mbedtls that - * contains it, if any. */ -static inline struct bufferevent_mbedtls * -upcast(struct bufferevent *bev) -{ - struct bufferevent_mbedtls *bev_o; - if (!BEV_IS_MBEDTLS(bev)) - return NULL; - bev_o = (void*)( ((char*)bev) - - evutil_offsetof(struct bufferevent_mbedtls, bev.bev)); - EVUTIL_ASSERT(BEV_IS_MBEDTLS(&bev_o->bev.bev)); - return bev_o; -} - -static inline void -put_error(struct bufferevent_mbedtls *bev_ssl, unsigned long err) -{ - if (bev_ssl->n_errors == NUM_ERRORS) - return; - /* The error type according to openssl is "unsigned long", but - openssl never uses more than 32 bits of it. It _can't_ use more - than 32 bits of it, since it needs to report errors on systems - where long is only 32 bits. - */ - bev_ssl->errors[bev_ssl->n_errors++] = (ev_uint32_t) err; -} - -/* Have the base communications channel (either the underlying bufferevent or - * ev_read and ev_write) start reading. Take the read-blocked-on-write flag - * into account. */ -static int -start_reading(struct bufferevent_mbedtls *bev_ssl) -{ - if (bev_ssl->underlying) { - bufferevent_unsuspend_read_(bev_ssl->underlying, - BEV_SUSPEND_FILT_READ); - return 0; - } else { - struct bufferevent *bev = &bev_ssl->bev.bev; - int r; - r = bufferevent_add_event_(&bev->ev_read, &bev->timeout_read); - if (r == 0 && bev_ssl->read_blocked_on_write) - r = bufferevent_add_event_(&bev->ev_write, - &bev->timeout_write); - return r; - } -} - -/* Have the base communications channel (either the underlying bufferevent or - * ev_read and ev_write) start writing. Take the write-blocked-on-read flag - * into account. */ -static int -start_writing(struct bufferevent_mbedtls *bev_ssl) -{ - int r = 0; - if (bev_ssl->underlying) { - if (bev_ssl->write_blocked_on_read) { - bufferevent_unsuspend_read_(bev_ssl->underlying, - BEV_SUSPEND_FILT_READ); - } - } else { - struct bufferevent *bev = &bev_ssl->bev.bev; - r = bufferevent_add_event_(&bev->ev_write, &bev->timeout_write); - if (!r && bev_ssl->write_blocked_on_read) - r = bufferevent_add_event_(&bev->ev_read, - &bev->timeout_read); - } - return r; -} - static void -stop_reading(struct bufferevent_mbedtls *bev_ssl) -{ - if (bev_ssl->write_blocked_on_read) - return; - if (bev_ssl->underlying) { - bufferevent_suspend_read_(bev_ssl->underlying, - BEV_SUSPEND_FILT_READ); - } else { - struct bufferevent *bev = &bev_ssl->bev.bev; - event_del(&bev->ev_read); - } -} - -static void -stop_writing(struct bufferevent_mbedtls *bev_ssl) -{ - if (bev_ssl->read_blocked_on_write) - return; - if (bev_ssl->underlying) { - bufferevent_unsuspend_read_(bev_ssl->underlying, - BEV_SUSPEND_FILT_READ); - } else { - struct bufferevent *bev = &bev_ssl->bev.bev; - event_del(&bev->ev_write); - } -} - -static int -set_rbow(struct bufferevent_mbedtls *bev_ssl) -{ - if (!bev_ssl->underlying) - stop_reading(bev_ssl); - bev_ssl->read_blocked_on_write = 1; - return start_writing(bev_ssl); -} - -static int -set_wbor(struct bufferevent_mbedtls *bev_ssl) -{ - if (!bev_ssl->underlying) - stop_writing(bev_ssl); - bev_ssl->write_blocked_on_read = 1; - return start_reading(bev_ssl); -} - -static int -clear_rbow(struct bufferevent_mbedtls *bev_ssl) -{ - struct bufferevent *bev = &bev_ssl->bev.bev; - int r = 0; - bev_ssl->read_blocked_on_write = 0; - if (!(bev->enabled & EV_WRITE)) - stop_writing(bev_ssl); - if (bev->enabled & EV_READ) - r = start_reading(bev_ssl); - return r; -} - - -static int -clear_wbor(struct bufferevent_mbedtls *bev_ssl) -{ - struct bufferevent *bev = &bev_ssl->bev.bev; - int r = 0; - bev_ssl->write_blocked_on_read = 0; - if (!(bev->enabled & EV_READ)) - stop_reading(bev_ssl); - if (bev->enabled & EV_WRITE) - r = start_writing(bev_ssl); - return r; -} - -static void -conn_closed(struct bufferevent_mbedtls *bev_ssl, int when, int errcode, int ret) +conn_closed(struct bufferevent_ssl *bev_ssl, int when, int errcode, int ret) { int event = BEV_EVENT_ERROR; - //int dirty_shutdown = 0; char buf[100]; - if (when & BEV_EVENT_READING && ret == 0) - { + if (when & BEV_EVENT_READING && ret == 0) { if (bev_ssl->allow_dirty_shutdown) event = BEV_EVENT_EOF; } else { @@ -377,925 +218,181 @@ conn_closed(struct bufferevent_mbedtls *bev_ssl, int when, int errcode, int ret) case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: event = BEV_EVENT_EOF; break; - //case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS: - //case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS: case MBEDTLS_ERR_SSL_CLIENT_RECONNECT: event_warnx("BUG: Unsupported feature %d: %s", errcode, buf); break; default: /* should be impossible; treat as normal error. */ - event_warnx("BUG: Unexpected mbedtls error code %d: %s", errcode, buf); + event_warnx( + "BUG: Unexpected mbedtls error code %d: %s", errcode, buf); break; } - put_error(bev_ssl, errcode); + bufferevent_ssl_put_error(bev_ssl, errcode); } - - stop_reading(bev_ssl); - stop_writing(bev_ssl); + bufferevent_ssl_stop_reading(bev_ssl); + bufferevent_ssl_stop_writing(bev_ssl); bufferevent_run_eventcb_(&bev_ssl->bev.bev, when | event, 0); } -#define OP_MADE_PROGRESS 1 -#define OP_BLOCKED 2 -#define OP_ERR 4 - -/* Return a bitmask of OP_MADE_PROGRESS (if we read anything); OP_BLOCKED (if - we're now blocked); and OP_ERR (if an error occurred). */ -static int -do_read(struct bufferevent_mbedtls *bev_ssl, int n_to_read) { - /* Requires lock */ - struct bufferevent *bev = &bev_ssl->bev.bev; - struct evbuffer *input = bev->input; - int r, n, i, n_used = 0, atmost; - struct evbuffer_iovec space[2]; - int result = 0; - - if (bev_ssl->bev.read_suspended) - return 0; - - atmost = bufferevent_get_read_max_(&bev_ssl->bev); - if (n_to_read > atmost) - n_to_read = atmost; - - n = evbuffer_reserve_space(input, n_to_read, space, 2); - if (n < 0) - return OP_ERR; - - for (i=0; ibev.read_suspended) - break; - r = mbedtls_ssl_read(bev_ssl->ssl, space[i].iov_base, space[i].iov_len); - if (r>0) { - result |= OP_MADE_PROGRESS; - if (bev_ssl->read_blocked_on_write) - if (clear_rbow(bev_ssl) < 0) - return OP_ERR | result; - ++n_used; - space[i].iov_len = r; - } else { - int err = r; - print_err(err); - switch (err) { - case SSL_ERROR_WANT_READ: - /* Can't read until underlying has more data. */ - if (bev_ssl->read_blocked_on_write) - if (clear_rbow(bev_ssl) < 0) - return OP_ERR | result; - break; - case SSL_ERROR_WANT_WRITE: - /* This read operation requires a write, and the - * underlying is full */ - if (!bev_ssl->read_blocked_on_write) - if (set_rbow(bev_ssl) < 0) - return OP_ERR | result; - break; - default: - conn_closed(bev_ssl, BEV_EVENT_READING, err, r); - break; - } - result |= OP_BLOCKED; - break; /* out of the loop */ - } - } - - if (n_used) { - evbuffer_commit_space(input, space, n_used); - if (bev_ssl->underlying) - BEV_RESET_GENERIC_READ_TIMEOUT(bev); - } - - return result; -} - -/* Return a bitmask of OP_MADE_PROGRESS (if we wrote anything); OP_BLOCKED (if - we're now blocked); and OP_ERR (if an error occurred). */ static int -do_write(struct bufferevent_mbedtls *bev_ssl, int atmost) -{ - int i, r, n, n_written = 0; - struct bufferevent *bev = &bev_ssl->bev.bev; - struct evbuffer *output = bev->output; - struct evbuffer_iovec space[8]; - int result = 0; - - if (bev_ssl->last_write > 0) - atmost = bev_ssl->last_write; - else - atmost = bufferevent_get_write_max_(&bev_ssl->bev); - - n = evbuffer_peek(output, atmost, NULL, space, 8); - if (n < 0) - return OP_ERR | result; - - if (n > 8) - n = 8; - for (i=0; i < n; ++i) { - if (bev_ssl->bev.write_suspended) - break; - - /* SSL_write will (reasonably) return 0 if we tell it to - send 0 data. Skip this case so we don't interpret the - result as an error */ - if (space[i].iov_len == 0) - continue; - - r = mbedtls_ssl_write(bev_ssl->ssl, space[i].iov_base, - space[i].iov_len); - if (r > 0) { - result |= OP_MADE_PROGRESS; - if (bev_ssl->write_blocked_on_read) - if (clear_wbor(bev_ssl) < 0) - return OP_ERR | result; - n_written += r; - bev_ssl->last_write = -1; - } else { - int err = r; - print_err(err); - switch (err) { - case SSL_ERROR_WANT_WRITE: - /* Can't read until underlying has more data. */ - if (bev_ssl->write_blocked_on_read) - if (clear_wbor(bev_ssl) < 0) - return OP_ERR | result; - bev_ssl->last_write = space[i].iov_len; - break; - case SSL_ERROR_WANT_READ: - /* This read operation requires a write, and the - * underlying is full */ - if (!bev_ssl->write_blocked_on_read) - if (set_wbor(bev_ssl) < 0) - return OP_ERR | result; - bev_ssl->last_write = space[i].iov_len; - break; - default: - conn_closed(bev_ssl, BEV_EVENT_WRITING, err, r); - bev_ssl->last_write = -1; - break; - } - result |= OP_BLOCKED; - break; - } - } - if (n_written) { - evbuffer_drain(output, n_written); - if (bev_ssl->underlying) - BEV_RESET_GENERIC_WRITE_TIMEOUT(bev); - - bufferevent_trigger_nolock_(bev, EV_WRITE, BEV_OPT_DEFER_CALLBACKS); - } - return result; -} - -#define WRITE_FRAME 15000 - -#define READ_DEFAULT 4096 - -/* Try to figure out how many bytes to read; return 0 if we shouldn't be - * reading. */ -static int -bytes_to_read(struct bufferevent_mbedtls *bev) -{ - struct evbuffer *input = bev->bev.bev.input; - struct event_watermark *wm = &bev->bev.bev.wm_read; - int result = READ_DEFAULT; - ev_ssize_t limit; - /* XXX 99% of this is generic code that nearly all bufferevents will - * want. */ - - if (bev->write_blocked_on_read) { - return 0; - } - - if (! (bev->bev.bev.enabled & EV_READ)) { - return 0; - } - - if (bev->bev.read_suspended) { - return 0; - } - - if (wm->high) { - if (evbuffer_get_length(input) >= wm->high) { - return 0; - } - - result = wm->high - evbuffer_get_length(input); - } else { - result = READ_DEFAULT; - } - - /* Respect the rate limit */ - limit = bufferevent_get_read_max_(&bev->bev); - if (result > limit) { - result = limit; - } - - return result; -} - - -/* Things look readable. If write is blocked on read, write till it isn't. - * Read from the underlying buffer until we block or we hit our high-water - * mark. - */ -static void -consider_reading(struct bufferevent_mbedtls *bev_ssl) -{ - int r; - int n_to_read; - int all_result_flags = 0; - - while (bev_ssl->write_blocked_on_read) { - r = do_write(bev_ssl, WRITE_FRAME); - if (r & (OP_BLOCKED|OP_ERR)) - break; - } - if (bev_ssl->write_blocked_on_read) - return; - - n_to_read = bytes_to_read(bev_ssl); - - while (n_to_read) { - r = do_read(bev_ssl, n_to_read); - all_result_flags |= r; - - if (r & (OP_BLOCKED|OP_ERR)) - break; - - if (bev_ssl->bev.read_suspended) - break; - - /* Read all pending data. This won't hit the network - * again, and will (most importantly) put us in a state - * where we don't need to read anything else until the - * socket is readable again. It'll potentially make us - * overrun our read high-watermark (somewhat - * regrettable). The damage to the rate-limit has - * already been done, since OpenSSL went and read a - * whole SSL record anyway. */ - n_to_read = mbedtls_ssl_get_bytes_avail(bev_ssl->ssl); - - /* XXX This if statement is actually a bad bug, added to avoid - * XXX a worse bug. - * - * The bad bug: It can potentially cause resource unfairness - * by reading too much data from the underlying bufferevent; - * it can potentially cause read looping if the underlying - * bufferevent is a bufferevent_pair and deferred callbacks - * aren't used. - * - * The worse bug: If we didn't do this, then we would - * potentially not read any more from bev_ssl->underlying - * until more data arrived there, which could lead to us - * waiting forever. - */ - if (!n_to_read && bev_ssl->underlying) - n_to_read = bytes_to_read(bev_ssl); - } - - if (all_result_flags & OP_MADE_PROGRESS) { - struct bufferevent *bev = &bev_ssl->bev.bev; - - bufferevent_trigger_nolock_(bev, EV_READ, 0); - } - - if (!bev_ssl->underlying) { - /* Should be redundant, but let's avoid busy-looping */ - if (bev_ssl->bev.read_suspended || - !(bev_ssl->bev.bev.enabled & EV_READ)) { - event_del(&bev_ssl->bev.bev.ev_read); - } - } -} - -static void -consider_writing(struct bufferevent_mbedtls *bev_ssl) -{ - int r; - struct evbuffer *output = bev_ssl->bev.bev.output; - struct evbuffer *target = NULL; - struct event_watermark *wm = NULL; - - while (bev_ssl->read_blocked_on_write) { - r = do_read(bev_ssl, 1024); /* XXXX 1024 is a hack */ - if (r & OP_MADE_PROGRESS) { - struct bufferevent *bev = &bev_ssl->bev.bev; - - bufferevent_trigger_nolock_(bev, EV_READ, 0); - } - if (r & (OP_ERR|OP_BLOCKED)) - break; - } - if (bev_ssl->read_blocked_on_write) - return; - if (bev_ssl->underlying) { - target = bev_ssl->underlying->output; - wm = &bev_ssl->underlying->wm_write; - } - while ((bev_ssl->bev.bev.enabled & EV_WRITE) && - (! bev_ssl->bev.write_suspended) && - evbuffer_get_length(output) && - (!target || (! wm->high || evbuffer_get_length(target) < wm->high))) { - int n_to_write; - if (wm && wm->high) - n_to_write = wm->high - evbuffer_get_length(target); - else - n_to_write = WRITE_FRAME; - r = do_write(bev_ssl, n_to_write); - if (r & (OP_BLOCKED|OP_ERR)) - break; - } - - if (!bev_ssl->underlying) { - if (evbuffer_get_length(output) == 0) { - event_del(&bev_ssl->bev.bev.ev_write); - } else if (bev_ssl->bev.write_suspended || - !(bev_ssl->bev.bev.enabled & EV_WRITE)) { - /* Should be redundant, but let's avoid busy-looping */ - event_del(&bev_ssl->bev.bev.ev_write); - } - } -} - -static void -be_mbedtls_readcb(struct bufferevent *bev_base, void *ctx) -{ - struct bufferevent_mbedtls *bev_ssl = ctx; - consider_reading(bev_ssl); -} - -static void -be_mbedtls_writecb(struct bufferevent *bev_base, void *ctx) -{ - struct bufferevent_mbedtls *bev_ssl = ctx; - consider_writing(bev_ssl); -} - -static void -be_mbedtls_eventcb(struct bufferevent *bev_base, short what, void *ctx) -{ - struct bufferevent_mbedtls *bev_ssl = ctx; - int event = 0; - - if (what & BEV_EVENT_EOF) { - if (bev_ssl->allow_dirty_shutdown) - event = BEV_EVENT_EOF; - else - event = BEV_EVENT_ERROR; - } else if (what & BEV_EVENT_TIMEOUT) { - /* We sure didn't set this. Propagate it to the user. */ - event = what; - } else if (what & BEV_EVENT_ERROR) { - /* An error occurred on the connection. Propagate it to the user. */ - event = what; - } else if (what & BEV_EVENT_CONNECTED) { - /* Ignore it. We're saying SSL_connect() already, which will - eat it. */ - } - if (event) - bufferevent_run_eventcb_(&bev_ssl->bev.bev, event, 0); -} - -static void -be_mbedtls_readeventcb(evutil_socket_t fd, short what, void *ptr) -{ - struct bufferevent_mbedtls *bev_ssl = ptr; - bufferevent_incref_and_lock_(&bev_ssl->bev.bev); - if (what == EV_TIMEOUT) { - bufferevent_run_eventcb_(&bev_ssl->bev.bev, - BEV_EVENT_TIMEOUT|BEV_EVENT_READING, 0); - } else { - consider_reading(bev_ssl); - } - bufferevent_decref_and_unlock_(&bev_ssl->bev.bev); -} - -static void -be_mbedtls_writeeventcb(evutil_socket_t fd, short what, void *ptr) -{ - struct bufferevent_mbedtls *bev_ssl = ptr; - bufferevent_incref_and_lock_(&bev_ssl->bev.bev); - if (what == EV_TIMEOUT) { - bufferevent_run_eventcb_(&bev_ssl->bev.bev, - BEV_EVENT_TIMEOUT|BEV_EVENT_WRITING, 0); - } else { - consider_writing(bev_ssl); - } - bufferevent_decref_and_unlock_(&bev_ssl->bev.bev); -} - -static evutil_socket_t -be_mbedtls_auto_fd(struct bufferevent_mbedtls *bev_ssl, evutil_socket_t fd) +be_mbedtls_bio_set_fd(struct bufferevent_ssl *bev_ssl, evutil_socket_t fd) { + struct mbedtls_context *ctx = bev_ssl->ssl; if (!bev_ssl->underlying) { - struct bufferevent *bev = &bev_ssl->bev.bev; - if (event_initialized(&bev->ev_read) && fd < 0) { - fd = event_get_fd(&bev->ev_read); - } - } - return fd; -} - -static int -set_open_callbacks(struct bufferevent_mbedtls *bev_ssl, evutil_socket_t fd) -{ - if (bev_ssl->underlying) { - bufferevent_setcb(bev_ssl->underlying, - be_mbedtls_readcb, be_mbedtls_writecb, be_mbedtls_eventcb, - bev_ssl); - return 0; - } else { - struct bufferevent *bev = &bev_ssl->bev.bev; - int rpending=0, wpending=0, r1=0, r2=0; - - if (event_initialized(&bev->ev_read)) { - rpending = event_pending(&bev->ev_read, EV_READ, NULL); - wpending = event_pending(&bev->ev_write, EV_WRITE, NULL); - - event_del(&bev->ev_read); - event_del(&bev->ev_write); - } - - event_assign(&bev->ev_read, bev->ev_base, fd, - EV_READ|EV_PERSIST|EV_FINALIZE, - be_mbedtls_readeventcb, bev_ssl); - event_assign(&bev->ev_write, bev->ev_base, fd, - EV_WRITE|EV_PERSIST|EV_FINALIZE, - be_mbedtls_writeeventcb, bev_ssl); - - if (rpending) - r1 = bufferevent_add_event_(&bev->ev_read, &bev->timeout_read); - if (wpending) - r2 = bufferevent_add_event_(&bev->ev_write, &bev->timeout_write); - - return (r1<0 || r2<0) ? -1 : 0; - } -} - -static int -do_handshake(struct bufferevent_mbedtls *bev_ssl) -{ - int r; - - switch (bev_ssl->state) { - default: - case BUFFEREVENT_SSL_OPEN: - EVUTIL_ASSERT(0); - return -1; - case BUFFEREVENT_SSL_CONNECTING: - case BUFFEREVENT_SSL_ACCEPTING: - r = mbedtls_ssl_handshake(bev_ssl->ssl); - break; - } - - if (r==0) { - evutil_socket_t fd = event_get_fd(&bev_ssl->bev.bev.ev_read); - /* We're done! */ - bev_ssl->state = BUFFEREVENT_SSL_OPEN; - set_open_callbacks(bev_ssl, fd); /* XXXX handle failure */ - /* Call do_read and do_write as needed */ - bufferevent_enable(&bev_ssl->bev.bev, bev_ssl->bev.bev.enabled); - bufferevent_run_eventcb_(&bev_ssl->bev.bev, - BEV_EVENT_CONNECTED, 0); - return 1; + ctx->net.fd = fd; + mbedtls_ssl_set_bio( + ctx->ssl, &ctx->net, mbedtls_net_send, mbedtls_net_recv, NULL); } else { - int err = r; - print_err(err); - switch (err) { - case SSL_ERROR_WANT_WRITE: - stop_reading(bev_ssl); - return start_writing(bev_ssl); - case SSL_ERROR_WANT_READ: - stop_writing(bev_ssl); - return start_reading(bev_ssl); - default: - conn_closed(bev_ssl, BEV_EVENT_READING, err, r); - return -1; - } + mbedtls_ssl_set_bio(ctx->ssl, bev_ssl->underlying, + bio_bufferevent_write, bio_bufferevent_read, NULL); } + return 0; } -static void -be_mbedtls_handshakecb(struct bufferevent *bev_base, void *ctx) +int +bufferevent_mbedtls_get_allow_dirty_shutdown(struct bufferevent *bev) { - struct bufferevent_mbedtls *bev_ssl = ctx; - do_handshake(bev_ssl);/* XXX handle failure */ + return bufferevent_ssl_get_allow_dirty_shutdown(bev); } -static void -be_mbedtls_handshakeeventcb(evutil_socket_t fd, short what, void *ptr) +void +bufferevent_mbedtls_set_allow_dirty_shutdown( + struct bufferevent *bev, int allow_dirty_shutdown) { - struct bufferevent_mbedtls *bev_ssl = ptr; - - bufferevent_incref_and_lock_(&bev_ssl->bev.bev); - if (what & EV_TIMEOUT) { - bufferevent_run_eventcb_(&bev_ssl->bev.bev, BEV_EVENT_TIMEOUT, 0); - } else - do_handshake(bev_ssl);/* XXX handle failure */ - bufferevent_decref_and_unlock_(&bev_ssl->bev.bev); + bufferevent_ssl_set_allow_dirty_shutdown(bev, allow_dirty_shutdown); } -static int -set_handshake_callbacks(struct bufferevent_mbedtls *bev_ssl, evutil_socket_t fd) +mbedtls_ssl_context * +bufferevent_mbedtls_get_ssl(struct bufferevent *bufev) { - if (bev_ssl->underlying) { - bufferevent_setcb(bev_ssl->underlying, - be_mbedtls_handshakecb, be_mbedtls_handshakecb, - be_mbedtls_eventcb, - bev_ssl); - - if (fd < 0) - return 0; - - if (bufferevent_setfd(bev_ssl->underlying, fd)) - return 1; - - return do_handshake(bev_ssl); - } else { - struct bufferevent *bev = &bev_ssl->bev.bev; - - if (event_initialized(&bev->ev_read)) { - event_del(&bev->ev_read); - event_del(&bev->ev_write); - } - - event_assign(&bev->ev_read, bev->ev_base, fd, - EV_READ|EV_PERSIST|EV_FINALIZE, - be_mbedtls_handshakeeventcb, bev_ssl); - event_assign(&bev->ev_write, bev->ev_base, fd, - EV_WRITE|EV_PERSIST|EV_FINALIZE, - be_mbedtls_handshakeeventcb, bev_ssl); - if (fd >= 0) - bufferevent_enable(bev, bev->enabled); - return 0; - } + struct mbedtls_context *ctx = NULL; + struct bufferevent_ssl *bev_ssl = bufferevent_ssl_upcast(bufev); + if (!bev_ssl) + return NULL; + ctx = bev_ssl->ssl; + return ctx->ssl; } int -bufferevent_mbedtls_renegotiate(struct bufferevent *bev) +bufferevent_mbedtls_renegotiate(struct bufferevent *bufev) { - struct bufferevent_mbedtls *bev_ssl = upcast(bev); + struct bufferevent_ssl *bev_ssl = bufferevent_ssl_upcast(bufev); if (!bev_ssl) return -1; - if (mbedtls_ssl_renegotiate(bev_ssl->ssl) < 0) - return -1; - bev_ssl->state = BUFFEREVENT_SSL_CONNECTING; - if (set_handshake_callbacks(bev_ssl, be_mbedtls_auto_fd(bev_ssl, -1)) < 0) - return -1; - if (!bev_ssl->underlying) - return do_handshake(bev_ssl); - return 0; -} - -static void -be_mbedtls_outbuf_cb(struct evbuffer *buf, - const struct evbuffer_cb_info *cbinfo, void *arg) -{ - struct bufferevent_mbedtls *bev_ssl = arg; - int r = 0; - /* XXX need to hold a reference here. */ - - if (cbinfo->n_added && bev_ssl->state == BUFFEREVENT_SSL_OPEN) { - if (cbinfo->orig_size == 0) - r = bufferevent_add_event_(&bev_ssl->bev.bev.ev_write, - &bev_ssl->bev.bev.timeout_write); - - if (bev_ssl->underlying) - consider_writing(bev_ssl); - } - /* XXX Handle r < 0 */ - (void)r; -} - - -static int -be_mbedtls_enable(struct bufferevent *bev, short events) -{ - struct bufferevent_mbedtls *bev_ssl = upcast(bev); - int r1 = 0, r2 = 0; - - if (events & EV_READ) - r1 = start_reading(bev_ssl); - if (events & EV_WRITE) - r2 = start_writing(bev_ssl); - - if (bev_ssl->underlying) { - if (events & EV_READ) - BEV_RESET_GENERIC_READ_TIMEOUT(bev); - if (events & EV_WRITE) - BEV_RESET_GENERIC_WRITE_TIMEOUT(bev); - - if (events & EV_READ) - consider_reading(bev_ssl); - if (events & EV_WRITE) - consider_writing(bev_ssl); - } - return (r1 < 0 || r2 < 0) ? -1 : 0; -} - -static int -be_mbedtls_disable(struct bufferevent *bev, short events) -{ - struct bufferevent_mbedtls *bev_ssl = upcast(bev); - - if (events & EV_READ) - stop_reading(bev_ssl); - if (events & EV_WRITE) - stop_writing(bev_ssl); - - if (bev_ssl->underlying) { - if (events & EV_READ) - BEV_DEL_GENERIC_READ_TIMEOUT(bev); - if (events & EV_WRITE) - BEV_DEL_GENERIC_WRITE_TIMEOUT(bev); - } - return 0; + return bufferevent_ssl_renegotiate_impl(bufev); } -static void -be_mbedtls_unlink(struct bufferevent *bev) -{ - struct bufferevent_mbedtls *bev_ssl = upcast(bev); - - if (bev_ssl->bev.options & BEV_OPT_CLOSE_ON_FREE) { - if (bev_ssl->underlying) { - if (BEV_UPCAST(bev_ssl->underlying)->refcnt < 2) { - event_warnx("BEV_OPT_CLOSE_ON_FREE set on an " - "bufferevent with too few references"); - } else { - mbedtls_ssl_set_bio(bev_ssl->ssl, NULL, NULL, NULL, NULL); - bufferevent_free(bev_ssl->underlying); - /* We still have a reference to it, via our - * BIO. So we don't drop this. */ - // bev_ssl->underlying = NULL; - } - } - } else { - if (bev_ssl->underlying) { - if (bev_ssl->underlying->errorcb == be_mbedtls_eventcb) - bufferevent_setcb(bev_ssl->underlying, - NULL,NULL,NULL,NULL); - bufferevent_unsuspend_read_(bev_ssl->underlying, - BEV_SUSPEND_FILT_READ); - } - } -} - -static void -be_mbedtls_destruct(struct bufferevent *bev) -{ - struct bufferevent_mbedtls *bev_ssl = upcast(bev); - - if (bev_ssl->bev.options & BEV_OPT_CLOSE_ON_FREE) { - if (! bev_ssl->underlying) { - evutil_socket_t fd = (evutil_socket_t)bev_ssl->net_ctx.fd; - if (fd >= 0) - evutil_closesocket(fd); - } - mbedtls_ssl_free(bev_ssl->ssl); - } -} - -static int -be_mbedtls_adj_timeouts(struct bufferevent *bev) -{ - struct bufferevent_mbedtls *bev_ssl = upcast(bev); - - if (bev_ssl->underlying) { - return bufferevent_generic_adj_timeouts_(bev); - } else { - return bufferevent_generic_adj_existing_timeouts_(bev); - } -} - -static int -be_mbedtls_flush(struct bufferevent *bufev, - short iotype, enum bufferevent_flush_mode mode) -{ - /* XXXX Implement this. */ - return 0; -} - -static int -be_mbedtls_set_fd(struct bufferevent_mbedtls *bev_ssl, - enum bufferevent_ssl_state state, evutil_socket_t fd) -{ - if (!bev_ssl->underlying) { - bev_ssl->net_ctx.fd = fd; - mbedtls_ssl_set_bio(bev_ssl->ssl, &(bev_ssl->net_ctx), mbedtls_net_send, mbedtls_net_recv, NULL); - } else { - mbedtls_ssl_set_bio(bev_ssl->ssl, bev_ssl->underlying, bio_bufferevent_write, bio_bufferevent_read, NULL); - } - - bev_ssl->state = state; - - switch (state) { - case BUFFEREVENT_SSL_ACCEPTING: - if (bev_ssl->ssl->conf->endpoint != MBEDTLS_SSL_IS_SERVER) - return -1; - if (set_handshake_callbacks(bev_ssl, fd) < 0) - return -1; - break; - case BUFFEREVENT_SSL_CONNECTING: - if (bev_ssl->ssl->conf->endpoint != MBEDTLS_SSL_IS_CLIENT) - return -1; - if (set_handshake_callbacks(bev_ssl, fd) < 0) - return -1; - break; - case BUFFEREVENT_SSL_OPEN: - if (set_open_callbacks(bev_ssl, fd) < 0) - return -1; - break; - default: - return -1; - } - - return 0; -} - -static int -be_mbedtls_ctrl(struct bufferevent *bev, - enum bufferevent_ctrl_op op, union bufferevent_ctrl_data *data) -{ - struct bufferevent_mbedtls *bev_ssl = upcast(bev); - switch (op) { - case BEV_CTRL_SET_FD: - if (!bev_ssl->underlying) { - //bev_ssl->net_ctx.fd = data->fd; - //mbedtls_ssl_set_bio(bev_ssl->ssl, &(bev_ssl->net_ctx), mbedtls_net_send, mbedtls_net_recv, NULL); - } else { - //mbedtls_ssl_set_bio(bev_ssl->ssl, bev_ssl->underlying, bio_bufferevent_write, bio_bufferevent_read, NULL); - } - - return be_mbedtls_set_fd(bev_ssl, bev_ssl->old_state, data->fd); - case BEV_CTRL_GET_FD: - if (bev_ssl->underlying) { - data->fd = event_get_fd(&bev_ssl->underlying->ev_read); - } else { - data->fd = event_get_fd(&bev->ev_read); - } - return 0; - case BEV_CTRL_GET_UNDERLYING: - data->ptr = bev_ssl->underlying; - return 0; - case BEV_CTRL_CANCEL_ALL: - default: - return -1; - } -} - -SSL * -bufferevent_mbedtls_get_ssl(struct bufferevent *bufev) +unsigned long +bufferevent_get_mbedtls_error(struct bufferevent *bufev) { - struct bufferevent_mbedtls *bev_ssl = upcast(bufev); + struct bufferevent_ssl *bev_ssl = bufferevent_ssl_upcast(bufev); if (!bev_ssl) - return NULL; - return bev_ssl->ssl; -} + return -1; + return bufferevent_get_ssl_error(bufev); +} + +static struct le_ssl_ops le_mbedtls_ops = { + mbedtls_context_init, + mbedtls_context_free, + (void (*)(void *))mbedtls_ssl_free, + mbedtls_context_renegotiate, + mbedtls_context_write, + mbedtls_context_read, + mbedtls_context_pending, + mbedtls_context_handshake, + mbedtls_get_error, + mbedtls_clear_error, + mbedtls_clear, + mbedtls_set_ssl_noops, + mbedtls_set_ssl_noops, + mbedtls_is_ok, + mbedtls_is_want_read, + mbedtls_is_want_write, + be_mbedtls_get_fd, + be_mbedtls_bio_set_fd, + mbedtls_set_ssl_noops, + (void (*)(struct bufferevent_ssl *))mbedtls_set_ssl_noops, + (void (*)(struct bufferevent_ssl *))mbedtls_set_ssl_noops, + conn_closed, + print_err, +}; -static struct bufferevent * -bufferevent_mbedtls_new_impl(struct event_base *base, - struct bufferevent *underlying, - evutil_socket_t fd, - SSL *ssl, - enum bufferevent_ssl_state state, - int options) +struct bufferevent * +bufferevent_mbedtls_filter_new(struct event_base *base, + struct bufferevent *underlying, mbedtls_ssl_context *ssl, + enum bufferevent_ssl_state state, int options) { - struct bufferevent_mbedtls *bev_ssl = NULL; - struct bufferevent_private *bev_p = NULL; - int tmp_options = options & ~BEV_OPT_THREADSAFE; - - /* Only one can be set. */ - if (underlying != NULL && fd >= 0) - goto err; - - if (!(bev_ssl = mm_calloc(1, sizeof(struct bufferevent_mbedtls)))) - goto err; - - bev_p = &bev_ssl->bev; + struct bufferevent *bev; - if (bufferevent_init_common_(bev_p, base, - &bufferevent_ops_mbedtls, tmp_options) < 0) + if (!underlying) goto err; - bev_ssl->underlying = underlying; - bev_ssl->ssl = ssl; + bev = bufferevent_ssl_new_impl( + base, underlying, -1, ssl, state, options, &le_mbedtls_ops); - bev_ssl->outbuf_cb = evbuffer_add_cb(bev_p->bev.output, - be_mbedtls_outbuf_cb, bev_ssl); - - if (options & BEV_OPT_THREADSAFE) - bufferevent_enable_locking_(&bev_ssl->bev.bev, NULL); - - if (underlying) { - bufferevent_init_generic_timeout_cbs_(&bev_ssl->bev.bev); - bufferevent_incref_(underlying); + if (bev) { + be_mbedtls_bio_set_fd(bufferevent_ssl_upcast(bev), -1); } - bev_ssl->old_state = state; - bev_ssl->last_write = -1; - - fd = be_mbedtls_auto_fd(bev_ssl, fd); - if (be_mbedtls_set_fd(bev_ssl, state, fd)) - goto err; - - if (underlying) { - bufferevent_setwatermark(underlying, EV_READ, 0, 0); - bufferevent_enable(underlying, EV_READ|EV_WRITE); - if (state == BUFFEREVENT_SSL_OPEN) - bufferevent_suspend_read_(underlying, - BEV_SUSPEND_FILT_READ); - } + return bev; - return &bev_ssl->bev.bev; err: if (options & BEV_OPT_CLOSE_ON_FREE) mbedtls_ssl_free(ssl); - if (bev_ssl) { - bev_ssl->ssl = NULL; - bufferevent_free(&bev_ssl->bev.bev); - } return NULL; } struct bufferevent * -bufferevent_mbedtls_filter_new(struct event_base *base, - struct bufferevent *underlying, - SSL *ssl, - enum bufferevent_ssl_state state, - int options) +bufferevent_mbedtls_socket_new(struct event_base *base, evutil_socket_t fd, + mbedtls_ssl_context *ssl, enum bufferevent_ssl_state state, int options) { + long have_fd = -1; struct bufferevent *bev; - if (!underlying) - goto err; - - bev = bufferevent_mbedtls_new_impl( - base, underlying, -1, ssl, state, options); - return bev; - -err: - if (options & BEV_OPT_CLOSE_ON_FREE) - mbedtls_ssl_free(ssl); - return NULL; -} + if (ssl->p_bio) { + /* The SSL is already configured with bio. */ + if (ssl->f_send == mbedtls_net_send && + ssl->f_recv == mbedtls_net_recv) { + have_fd = ((mbedtls_net_context *)ssl->p_bio)->fd; + } else if (ssl->f_send == bio_bufferevent_write && + ssl->f_recv == bio_bufferevent_read) { + have_fd = bufferevent_getfd(ssl->p_bio); + } else { + /* We don't known the fd. */ + have_fd = LONG_MAX; + } + } -struct bufferevent * -bufferevent_mbedtls_socket_new(struct event_base *base, - evutil_socket_t fd, - SSL *ssl, - enum bufferevent_ssl_state state, - int options) -{ + if (have_fd >= 0) { + if (fd < 0) { + /* We should learn the fd from the SSL. */ + fd = (evutil_socket_t)have_fd; + } else if (have_fd == (long)fd) { + /* We already know the fd from the SSL; do nothing */ + } else { + /* We specified an fd different from that of the SSL. + This is probably an error on our part. Fail. */ + goto err; + } + } else { if (fd >= 0) { /* ... and we have an fd we want to use. */ } else { /* Leave the fd unset. */ } + } - return bufferevent_mbedtls_new_impl( - base, NULL, fd, ssl, state, options); - -} - -int -bufferevent_mbedtls_get_allow_dirty_shutdown(struct bufferevent *bev) -{ - int allow_dirty_shutdown = -1; - struct bufferevent_mbedtls *bev_ssl; - BEV_LOCK(bev); - bev_ssl = upcast(bev); - if (bev_ssl) - allow_dirty_shutdown = bev_ssl->allow_dirty_shutdown; - BEV_UNLOCK(bev); - return allow_dirty_shutdown; -} - -void -bufferevent_mbedtls_set_allow_dirty_shutdown(struct bufferevent *bev, - int allow_dirty_shutdown) -{ - struct bufferevent_mbedtls *bev_ssl; - BEV_LOCK(bev); - bev_ssl = upcast(bev); - if (bev_ssl) - bev_ssl->allow_dirty_shutdown = !!allow_dirty_shutdown; - BEV_UNLOCK(bev); -} + bev = bufferevent_ssl_new_impl( + base, NULL, fd, ssl, state, options, &le_mbedtls_ops); -unsigned long -bufferevent_get_mbedtls_error(struct bufferevent *bev) -{ - unsigned long err = 0; - struct bufferevent_mbedtls *bev_ssl; - BEV_LOCK(bev); - bev_ssl = upcast(bev); - if (bev_ssl && bev_ssl->n_errors) { - err = bev_ssl->errors[--bev_ssl->n_errors]; + if (bev) { + be_mbedtls_bio_set_fd(bufferevent_ssl_upcast(bev), fd); } - BEV_UNLOCK(bev); - return err; + + return bev; +err: + return NULL; } diff --git a/bufferevent_openssl.c b/bufferevent_openssl.c index b51b834b..a88ae891 100644 --- a/bufferevent_openssl.c +++ b/bufferevent_openssl.c @@ -24,48 +24,17 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -// Get rid of OSX 10.7 and greater deprecation warnings. -#if defined(__APPLE__) && defined(__clang__) -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -#endif - -#include "event2/event-config.h" -#include "evconfig-private.h" - -#include - -#ifdef EVENT__HAVE_SYS_TIME_H -#include -#endif - -#include -#include -#include #include -#ifdef EVENT__HAVE_STDARG_H -#include -#endif -#ifdef EVENT__HAVE_UNISTD_H -#include -#endif -#ifdef _WIN32 -#include -#endif +#include +#include +#include "openssl-compat.h" #include "event2/bufferevent.h" #include "event2/bufferevent_struct.h" -#include "event2/bufferevent_ssl.h" #include "event2/buffer.h" -#include "event2/event.h" -#include "mm-internal.h" -#include "bufferevent-internal.h" -#include "log-internal.h" - -#include -#include -#include "openssl-compat.h" +#include "ssl-compat.h" /* * Define an OpenSSL bio that targets a bufferevent. @@ -102,7 +71,10 @@ print_err(int val) } } #else -#define print_err(v) ((void)0) +static void +print_err(int val) +{ +} #endif /* Called to initialize a new BIO */ @@ -172,7 +144,7 @@ bio_bufferevent_write(BIO *b, const char *in, int inlen) /* Copy only as much data onto the output buffer as can fit under the * high-water mark. */ - if (bufev->wm_write.high && bufev->wm_write.high <= (outlen+inlen)) { + if (bufev->wm_write.high && bufev->wm_write.high <= (outlen + inlen)) { if (bufev->wm_write.high <= outlen) { /* If no data can fit, we'll need to retry later. */ BIO_set_retry_write(b); @@ -265,235 +237,8 @@ BIO_new_bufferevent(struct bufferevent *bufferevent) return result; } -/* -------------------- - Now, here's the OpenSSL-based implementation of bufferevent. - - The implementation comes in two flavors: one that connects its SSL object - to an underlying bufferevent using a BIO_bufferevent, and one that has the - SSL object connect to a socket directly. The latter should generally be - faster, except on Windows, where your best bet is using a - bufferevent_async. - - (OpenSSL supports many other BIO types, too. But we can't use any unless - we have a good way to get notified when they become readable/writable.) - -------------------- */ - -struct bio_data_counts { - unsigned long n_written; - unsigned long n_read; -}; - -struct bufferevent_openssl { - /* Shared fields with common bufferevent implementation code. - If we were set up with an underlying bufferevent, we use the - events here as timers only. If we have an SSL, then we use - the events as socket events. - */ - struct bufferevent_private bev; - /* An underlying bufferevent that we're directing our output to. - If it's NULL, then we're connected to an fd, not an evbuffer. */ - struct bufferevent *underlying; - /* The SSL object doing our encryption. */ - SSL *ssl; - - /* A callback that's invoked when data arrives on our outbuf so we - know to write data to the SSL. */ - struct evbuffer_cb_entry *outbuf_cb; - - /* A count of how much data the bios have read/written total. Used - for rate-limiting. */ - struct bio_data_counts counts; - - /* If this value is greater than 0, then the last SSL_write blocked, - * and we need to try it again with this many bytes. */ - ev_ssize_t last_write; - -#define NUM_ERRORS 3 - ev_uint32_t errors[NUM_ERRORS]; - - /* When we next get available space, we should say "read" instead of - "write". This can happen if there's a renegotiation during a read - operation. */ - unsigned read_blocked_on_write : 1; - /* When we next get data, we should say "write" instead of "read". */ - unsigned write_blocked_on_read : 1; - /* Treat TCP close before SSL close on SSL >= v3 as clean EOF. */ - unsigned allow_dirty_shutdown : 1; - /* XXX */ - unsigned n_errors : 2; - - /* Are we currently connecting, accepting, or doing IO? */ - unsigned state : 2; - /* If we reset fd, we sould reset state too */ - unsigned old_state : 2; -}; - -static int be_openssl_enable(struct bufferevent *, short); -static int be_openssl_disable(struct bufferevent *, short); -static void be_openssl_unlink(struct bufferevent *); -static void be_openssl_destruct(struct bufferevent *); -static int be_openssl_adj_timeouts(struct bufferevent *); -static int be_openssl_flush(struct bufferevent *bufev, - short iotype, enum bufferevent_flush_mode mode); -static int be_openssl_ctrl(struct bufferevent *, enum bufferevent_ctrl_op, union bufferevent_ctrl_data *); - -const struct bufferevent_ops bufferevent_ops_openssl = { - "ssl", - evutil_offsetof(struct bufferevent_openssl, bev.bev), - be_openssl_enable, - be_openssl_disable, - be_openssl_unlink, - be_openssl_destruct, - be_openssl_adj_timeouts, - be_openssl_flush, - be_openssl_ctrl, -}; - -/* Given a bufferevent, return a pointer to the bufferevent_openssl that - * contains it, if any. */ -static inline struct bufferevent_openssl * -upcast(struct bufferevent *bev) -{ - struct bufferevent_openssl *bev_o; - if (!BEV_IS_OPENSSL(bev)) - return NULL; - bev_o = (void*)( ((char*)bev) - - evutil_offsetof(struct bufferevent_openssl, bev.bev)); - EVUTIL_ASSERT(BEV_IS_OPENSSL(&bev_o->bev.bev)); - return bev_o; -} - -static inline void -put_error(struct bufferevent_openssl *bev_ssl, unsigned long err) -{ - if (bev_ssl->n_errors == NUM_ERRORS) - return; - /* The error type according to openssl is "unsigned long", but - openssl never uses more than 32 bits of it. It _can't_ use more - than 32 bits of it, since it needs to report errors on systems - where long is only 32 bits. - */ - bev_ssl->errors[bev_ssl->n_errors++] = (ev_uint32_t) err; -} - -/* Have the base communications channel (either the underlying bufferevent or - * ev_read and ev_write) start reading. Take the read-blocked-on-write flag - * into account. */ -static int -start_reading(struct bufferevent_openssl *bev_ssl) -{ - if (bev_ssl->underlying) { - bufferevent_unsuspend_read_(bev_ssl->underlying, - BEV_SUSPEND_FILT_READ); - return 0; - } else { - struct bufferevent *bev = &bev_ssl->bev.bev; - int r; - r = bufferevent_add_event_(&bev->ev_read, &bev->timeout_read); - if (r == 0 && bev_ssl->read_blocked_on_write) - r = bufferevent_add_event_(&bev->ev_write, - &bev->timeout_write); - return r; - } -} - -/* Have the base communications channel (either the underlying bufferevent or - * ev_read and ev_write) start writing. Take the write-blocked-on-read flag - * into account. */ -static int -start_writing(struct bufferevent_openssl *bev_ssl) -{ - int r = 0; - if (bev_ssl->underlying) { - if (bev_ssl->write_blocked_on_read) { - bufferevent_unsuspend_read_(bev_ssl->underlying, - BEV_SUSPEND_FILT_READ); - } - } else { - struct bufferevent *bev = &bev_ssl->bev.bev; - r = bufferevent_add_event_(&bev->ev_write, &bev->timeout_write); - if (!r && bev_ssl->write_blocked_on_read) - r = bufferevent_add_event_(&bev->ev_read, - &bev->timeout_read); - } - return r; -} - static void -stop_reading(struct bufferevent_openssl *bev_ssl) -{ - if (bev_ssl->write_blocked_on_read) - return; - if (bev_ssl->underlying) { - bufferevent_suspend_read_(bev_ssl->underlying, - BEV_SUSPEND_FILT_READ); - } else { - struct bufferevent *bev = &bev_ssl->bev.bev; - event_del(&bev->ev_read); - } -} - -static void -stop_writing(struct bufferevent_openssl *bev_ssl) -{ - if (bev_ssl->read_blocked_on_write) - return; - if (bev_ssl->underlying) { - bufferevent_unsuspend_read_(bev_ssl->underlying, - BEV_SUSPEND_FILT_READ); - } else { - struct bufferevent *bev = &bev_ssl->bev.bev; - event_del(&bev->ev_write); - } -} - -static int -set_rbow(struct bufferevent_openssl *bev_ssl) -{ - if (!bev_ssl->underlying) - stop_reading(bev_ssl); - bev_ssl->read_blocked_on_write = 1; - return start_writing(bev_ssl); -} - -static int -set_wbor(struct bufferevent_openssl *bev_ssl) -{ - if (!bev_ssl->underlying) - stop_writing(bev_ssl); - bev_ssl->write_blocked_on_read = 1; - return start_reading(bev_ssl); -} - -static int -clear_rbow(struct bufferevent_openssl *bev_ssl) -{ - struct bufferevent *bev = &bev_ssl->bev.bev; - int r = 0; - bev_ssl->read_blocked_on_write = 0; - if (!(bev->enabled & EV_WRITE)) - stop_writing(bev_ssl); - if (bev->enabled & EV_READ) - r = start_reading(bev_ssl); - return r; -} - - -static int -clear_wbor(struct bufferevent_openssl *bev_ssl) -{ - struct bufferevent *bev = &bev_ssl->bev.bev; - int r = 0; - bev_ssl->write_blocked_on_read = 0; - if (!(bev->enabled & EV_READ)) - stop_reading(bev_ssl); - if (bev->enabled & EV_WRITE) - r = start_writing(bev_ssl); - return r; -} - -static void -conn_closed(struct bufferevent_openssl *bev_ssl, int when, int errcode, int ret) +conn_closed(struct bufferevent_ssl *bev_ssl, int when, int errcode, int ret) { int event = BEV_EVENT_ERROR; int dirty_shutdown = 0; @@ -511,15 +256,15 @@ conn_closed(struct bufferevent_openssl *bev_ssl, int when, int errcode, int ret) /* IO error; possibly a dirty shutdown. */ if ((ret == 0 || ret == -1) && ERR_peek_error() == 0) dirty_shutdown = 1; - put_error(bev_ssl, errcode); + bufferevent_ssl_put_error(bev_ssl, errcode); break; case SSL_ERROR_SSL: /* Protocol error. */ - put_error(bev_ssl, errcode); + bufferevent_ssl_put_error(bev_ssl, errcode); break; case SSL_ERROR_WANT_X509_LOOKUP: /* XXXX handle this. */ - put_error(bev_ssl, errcode); + bufferevent_ssl_put_error(bev_ssl, errcode); break; case SSL_ERROR_NONE: case SSL_ERROR_WANT_READ: @@ -533,14 +278,14 @@ conn_closed(struct bufferevent_openssl *bev_ssl, int when, int errcode, int ret) } while ((err = ERR_get_error())) { - put_error(bev_ssl, err); + bufferevent_ssl_put_error(bev_ssl, err); } if (dirty_shutdown && bev_ssl->allow_dirty_shutdown) event = BEV_EVENT_EOF; - stop_reading(bev_ssl); - stop_writing(bev_ssl); + bufferevent_ssl_stop_reading(bev_ssl); + bufferevent_ssl_stop_writing(bev_ssl); /* when is BEV_EVENT_{READING|WRITING} */ event = when | event; @@ -548,7 +293,7 @@ conn_closed(struct bufferevent_openssl *bev_ssl, int when, int errcode, int ret) } static void -init_bio_counts(struct bufferevent_openssl *bev_ssl) +init_bio_counts(struct bufferevent_ssl *bev_ssl) { BIO *rbio, *wbio; @@ -559,7 +304,7 @@ init_bio_counts(struct bufferevent_openssl *bev_ssl) } static inline void -decrement_buckets(struct bufferevent_openssl *bev_ssl) +decrement_buckets(struct bufferevent_ssl *bev_ssl) { unsigned long num_w = BIO_number_written(SSL_get_wbio(bev_ssl->ssl)); unsigned long num_r = BIO_number_read(SSL_get_rbio(bev_ssl->ssl)); @@ -574,841 +319,108 @@ decrement_buckets(struct bufferevent_openssl *bev_ssl) bev_ssl->counts.n_read = num_r; } -#define OP_MADE_PROGRESS 1 -#define OP_BLOCKED 2 -#define OP_ERR 4 - -/* Return a bitmask of OP_MADE_PROGRESS (if we read anything); OP_BLOCKED (if - we're now blocked); and OP_ERR (if an error occurred). */ -static int -do_read(struct bufferevent_openssl *bev_ssl, int n_to_read) { - /* Requires lock */ - struct bufferevent *bev = &bev_ssl->bev.bev; - struct evbuffer *input = bev->input; - int r, n, i, n_used = 0, atmost; - struct evbuffer_iovec space[2]; - int result = 0; - - if (bev_ssl->bev.read_suspended) - return 0; - - atmost = bufferevent_get_read_max_(&bev_ssl->bev); - if (n_to_read > atmost) - n_to_read = atmost; - - n = evbuffer_reserve_space(input, n_to_read, space, 2); - if (n < 0) - return OP_ERR; - - for (i=0; ibev.read_suspended) - break; - ERR_clear_error(); - r = SSL_read(bev_ssl->ssl, space[i].iov_base, space[i].iov_len); - if (r>0) { - result |= OP_MADE_PROGRESS; - if (bev_ssl->read_blocked_on_write) - if (clear_rbow(bev_ssl) < 0) - return OP_ERR | result; - ++n_used; - space[i].iov_len = r; - decrement_buckets(bev_ssl); - } else { - int err = SSL_get_error(bev_ssl->ssl, r); - print_err(err); - switch (err) { - case SSL_ERROR_WANT_READ: - /* Can't read until underlying has more data. */ - if (bev_ssl->read_blocked_on_write) - if (clear_rbow(bev_ssl) < 0) - return OP_ERR | result; - break; - case SSL_ERROR_WANT_WRITE: - /* This read operation requires a write, and the - * underlying is full */ - if (!bev_ssl->read_blocked_on_write) - if (set_rbow(bev_ssl) < 0) - return OP_ERR | result; - break; - default: - conn_closed(bev_ssl, BEV_EVENT_READING, err, r); - break; - } - result |= OP_BLOCKED; - break; /* out of the loop */ - } - } - - if (n_used) { - evbuffer_commit_space(input, space, n_used); - if (bev_ssl->underlying) - BEV_RESET_GENERIC_READ_TIMEOUT(bev); - } - - return result; -} - -/* Return a bitmask of OP_MADE_PROGRESS (if we wrote anything); OP_BLOCKED (if - we're now blocked); and OP_ERR (if an error occurred). */ -static int -do_write(struct bufferevent_openssl *bev_ssl, int atmost) -{ - int i, r, n, n_written = 0; - struct bufferevent *bev = &bev_ssl->bev.bev; - struct evbuffer *output = bev->output; - struct evbuffer_iovec space[8]; - int result = 0; - - if (bev_ssl->last_write > 0) - atmost = bev_ssl->last_write; - else - atmost = bufferevent_get_write_max_(&bev_ssl->bev); - - n = evbuffer_peek(output, atmost, NULL, space, 8); - if (n < 0) - return OP_ERR | result; - - if (n > 8) - n = 8; - for (i=0; i < n; ++i) { - if (bev_ssl->bev.write_suspended) - break; - - /* SSL_write will (reasonably) return 0 if we tell it to - send 0 data. Skip this case so we don't interpret the - result as an error */ - if (space[i].iov_len == 0) - continue; - - ERR_clear_error(); - r = SSL_write(bev_ssl->ssl, space[i].iov_base, - space[i].iov_len); - if (r > 0) { - result |= OP_MADE_PROGRESS; - if (bev_ssl->write_blocked_on_read) - if (clear_wbor(bev_ssl) < 0) - return OP_ERR | result; - n_written += r; - bev_ssl->last_write = -1; - decrement_buckets(bev_ssl); - } else { - int err = SSL_get_error(bev_ssl->ssl, r); - print_err(err); - switch (err) { - case SSL_ERROR_WANT_WRITE: - /* Can't read until underlying has more data. */ - if (bev_ssl->write_blocked_on_read) - if (clear_wbor(bev_ssl) < 0) - return OP_ERR | result; - bev_ssl->last_write = space[i].iov_len; - break; - case SSL_ERROR_WANT_READ: - /* This read operation requires a write, and the - * underlying is full */ - if (!bev_ssl->write_blocked_on_read) - if (set_wbor(bev_ssl) < 0) - return OP_ERR | result; - bev_ssl->last_write = space[i].iov_len; - break; - default: - conn_closed(bev_ssl, BEV_EVENT_WRITING, err, r); - bev_ssl->last_write = -1; - break; - } - result |= OP_BLOCKED; - break; - } - } - if (n_written) { - evbuffer_drain(output, n_written); - if (bev_ssl->underlying) - BEV_RESET_GENERIC_WRITE_TIMEOUT(bev); - - bufferevent_trigger_nolock_(bev, EV_WRITE, BEV_OPT_DEFER_CALLBACKS); - } - return result; -} - -#define WRITE_FRAME 15000 - -#define READ_DEFAULT 4096 - -/* Try to figure out how many bytes to read; return 0 if we shouldn't be - * reading. */ -static int -bytes_to_read(struct bufferevent_openssl *bev) -{ - struct evbuffer *input = bev->bev.bev.input; - struct event_watermark *wm = &bev->bev.bev.wm_read; - int result = READ_DEFAULT; - ev_ssize_t limit; - /* XXX 99% of this is generic code that nearly all bufferevents will - * want. */ - - if (bev->write_blocked_on_read) { - return 0; - } - - if (! (bev->bev.bev.enabled & EV_READ)) { - return 0; - } - - if (bev->bev.read_suspended) { - return 0; - } - - if (wm->high) { - if (evbuffer_get_length(input) >= wm->high) { - return 0; - } - - result = wm->high - evbuffer_get_length(input); - } else { - result = READ_DEFAULT; - } - - /* Respect the rate limit */ - limit = bufferevent_get_read_max_(&bev->bev); - if (result > limit) { - result = limit; - } - - return result; -} - - -/* Things look readable. If write is blocked on read, write till it isn't. - * Read from the underlying buffer until we block or we hit our high-water - * mark. - */ -static void -consider_reading(struct bufferevent_openssl *bev_ssl) +static void * +SSL_init(void *ssl) { - int r; - int n_to_read; - int all_result_flags = 0; - - while (bev_ssl->write_blocked_on_read) { - r = do_write(bev_ssl, WRITE_FRAME); - if (r & (OP_BLOCKED|OP_ERR)) - break; - } - if (bev_ssl->write_blocked_on_read) - return; - - n_to_read = bytes_to_read(bev_ssl); - - while (n_to_read) { - r = do_read(bev_ssl, n_to_read); - all_result_flags |= r; - - if (r & (OP_BLOCKED|OP_ERR)) - break; - - if (bev_ssl->bev.read_suspended) - break; - - /* Read all pending data. This won't hit the network - * again, and will (most importantly) put us in a state - * where we don't need to read anything else until the - * socket is readable again. It'll potentially make us - * overrun our read high-watermark (somewhat - * regrettable). The damage to the rate-limit has - * already been done, since OpenSSL went and read a - * whole SSL record anyway. */ - n_to_read = SSL_pending(bev_ssl->ssl); - - /* XXX This if statement is actually a bad bug, added to avoid - * XXX a worse bug. - * - * The bad bug: It can potentially cause resource unfairness - * by reading too much data from the underlying bufferevent; - * it can potentially cause read looping if the underlying - * bufferevent is a bufferevent_pair and deferred callbacks - * aren't used. - * - * The worse bug: If we didn't do this, then we would - * potentially not read any more from bev_ssl->underlying - * until more data arrived there, which could lead to us - * waiting forever. - */ - if (!n_to_read && bev_ssl->underlying) - n_to_read = bytes_to_read(bev_ssl); - } - - if (all_result_flags & OP_MADE_PROGRESS) { - struct bufferevent *bev = &bev_ssl->bev.bev; - - bufferevent_trigger_nolock_(bev, EV_READ, 0); - } - - if (!bev_ssl->underlying) { - /* Should be redundant, but let's avoid busy-looping */ - if (bev_ssl->bev.read_suspended || - !(bev_ssl->bev.bev.enabled & EV_READ)) { - event_del(&bev_ssl->bev.bev.ev_read); - } - } + return ssl; } static void -consider_writing(struct bufferevent_openssl *bev_ssl) +SSL_context_free(void *ssl, int flags) { - int r; - struct evbuffer *output = bev_ssl->bev.bev.output; - struct evbuffer *target = NULL; - struct event_watermark *wm = NULL; - - while (bev_ssl->read_blocked_on_write) { - r = do_read(bev_ssl, 1024); /* XXXX 1024 is a hack */ - if (r & OP_MADE_PROGRESS) { - struct bufferevent *bev = &bev_ssl->bev.bev; - - bufferevent_trigger_nolock_(bev, EV_READ, 0); - } - if (r & (OP_ERR|OP_BLOCKED)) - break; - } - if (bev_ssl->read_blocked_on_write) - return; - if (bev_ssl->underlying) { - target = bev_ssl->underlying->output; - wm = &bev_ssl->underlying->wm_write; - } - while ((bev_ssl->bev.bev.enabled & EV_WRITE) && - (! bev_ssl->bev.write_suspended) && - evbuffer_get_length(output) && - (!target || (! wm->high || evbuffer_get_length(target) < wm->high))) { - int n_to_write; - if (wm && wm->high) - n_to_write = wm->high - evbuffer_get_length(target); - else - n_to_write = WRITE_FRAME; - r = do_write(bev_ssl, n_to_write); - if (r & (OP_BLOCKED|OP_ERR)) - break; - } - - if (!bev_ssl->underlying) { - if (evbuffer_get_length(output) == 0) { - event_del(&bev_ssl->bev.bev.ev_write); - } else if (bev_ssl->bev.write_suspended || - !(bev_ssl->bev.bev.enabled & EV_WRITE)) { - /* Should be redundant, but let's avoid busy-looping */ - event_del(&bev_ssl->bev.bev.ev_write); - } - } -} - -static void -be_openssl_readcb(struct bufferevent *bev_base, void *ctx) -{ - struct bufferevent_openssl *bev_ssl = ctx; - consider_reading(bev_ssl); -} - -static void -be_openssl_writecb(struct bufferevent *bev_base, void *ctx) -{ - struct bufferevent_openssl *bev_ssl = ctx; - consider_writing(bev_ssl); -} - -static void -be_openssl_eventcb(struct bufferevent *bev_base, short what, void *ctx) -{ - struct bufferevent_openssl *bev_ssl = ctx; - int event = 0; - - if (what & BEV_EVENT_EOF) { - if (bev_ssl->allow_dirty_shutdown) - event = BEV_EVENT_EOF; - else - event = BEV_EVENT_ERROR; - } else if (what & BEV_EVENT_TIMEOUT) { - /* We sure didn't set this. Propagate it to the user. */ - event = what; - } else if (what & BEV_EVENT_ERROR) { - /* An error occurred on the connection. Propagate it to the user. */ - event = what; - } else if (what & BEV_EVENT_CONNECTED) { - /* Ignore it. We're saying SSL_connect() already, which will - eat it. */ - } - if (event) - bufferevent_run_eventcb_(&bev_ssl->bev.bev, event, 0); -} - -static void -be_openssl_readeventcb(evutil_socket_t fd, short what, void *ptr) -{ - struct bufferevent_openssl *bev_ssl = ptr; - bufferevent_incref_and_lock_(&bev_ssl->bev.bev); - if (what == EV_TIMEOUT) { - bufferevent_run_eventcb_(&bev_ssl->bev.bev, - BEV_EVENT_TIMEOUT|BEV_EVENT_READING, 0); - } else { - consider_reading(bev_ssl); - } - bufferevent_decref_and_unlock_(&bev_ssl->bev.bev); -} - -static void -be_openssl_writeeventcb(evutil_socket_t fd, short what, void *ptr) -{ - struct bufferevent_openssl *bev_ssl = ptr; - bufferevent_incref_and_lock_(&bev_ssl->bev.bev); - if (what == EV_TIMEOUT) { - bufferevent_run_eventcb_(&bev_ssl->bev.bev, - BEV_EVENT_TIMEOUT|BEV_EVENT_WRITING, 0); - } else { - consider_writing(bev_ssl); - } - bufferevent_decref_and_unlock_(&bev_ssl->bev.bev); -} - -static evutil_socket_t -be_openssl_auto_fd(struct bufferevent_openssl *bev_ssl, evutil_socket_t fd) -{ - if (!bev_ssl->underlying) { - struct bufferevent *bev = &bev_ssl->bev.bev; - if (event_initialized(&bev->ev_read) && fd < 0) { - fd = event_get_fd(&bev->ev_read); - } - } - return fd; + if (flags & BEV_OPT_CLOSE_ON_FREE) + SSL_free(ssl); } static int -set_open_callbacks(struct bufferevent_openssl *bev_ssl, evutil_socket_t fd) +SSL_is_ok(int err) { - if (bev_ssl->underlying) { - bufferevent_setcb(bev_ssl->underlying, - be_openssl_readcb, be_openssl_writecb, be_openssl_eventcb, - bev_ssl); - return 0; - } else { - struct bufferevent *bev = &bev_ssl->bev.bev; - int rpending=0, wpending=0, r1=0, r2=0; - - if (event_initialized(&bev->ev_read)) { - rpending = event_pending(&bev->ev_read, EV_READ, NULL); - wpending = event_pending(&bev->ev_write, EV_WRITE, NULL); - - event_del(&bev->ev_read); - event_del(&bev->ev_write); - } - - event_assign(&bev->ev_read, bev->ev_base, fd, - EV_READ|EV_PERSIST|EV_FINALIZE, - be_openssl_readeventcb, bev_ssl); - event_assign(&bev->ev_write, bev->ev_base, fd, - EV_WRITE|EV_PERSIST|EV_FINALIZE, - be_openssl_writeeventcb, bev_ssl); - - if (rpending) - r1 = bufferevent_add_event_(&bev->ev_read, &bev->timeout_read); - if (wpending) - r2 = bufferevent_add_event_(&bev->ev_write, &bev->timeout_write); - - return (r1<0 || r2<0) ? -1 : 0; - } + return err == 1; } static int -do_handshake(struct bufferevent_openssl *bev_ssl) +SSL_is_want_read(int err) { - int r; - - switch (bev_ssl->state) { - default: - case BUFFEREVENT_SSL_OPEN: - EVUTIL_ASSERT(0); - return -1; - case BUFFEREVENT_SSL_CONNECTING: - case BUFFEREVENT_SSL_ACCEPTING: - ERR_clear_error(); - r = SSL_do_handshake(bev_ssl->ssl); - break; - } - decrement_buckets(bev_ssl); - - if (r==1) { - evutil_socket_t fd = event_get_fd(&bev_ssl->bev.bev.ev_read); - /* We're done! */ - bev_ssl->state = BUFFEREVENT_SSL_OPEN; - set_open_callbacks(bev_ssl, fd); /* XXXX handle failure */ - /* Call do_read and do_write as needed */ - bufferevent_enable(&bev_ssl->bev.bev, bev_ssl->bev.bev.enabled); - bufferevent_run_eventcb_(&bev_ssl->bev.bev, - BEV_EVENT_CONNECTED, 0); - return 1; - } else { - int err = SSL_get_error(bev_ssl->ssl, r); - print_err(err); - switch (err) { - case SSL_ERROR_WANT_WRITE: - stop_reading(bev_ssl); - return start_writing(bev_ssl); - case SSL_ERROR_WANT_READ: - stop_writing(bev_ssl); - return start_reading(bev_ssl); - default: - conn_closed(bev_ssl, BEV_EVENT_READING, err, r); - return -1; - } - } -} - -static void -be_openssl_handshakecb(struct bufferevent *bev_base, void *ctx) -{ - struct bufferevent_openssl *bev_ssl = ctx; - do_handshake(bev_ssl);/* XXX handle failure */ -} - -static void -be_openssl_handshakeeventcb(evutil_socket_t fd, short what, void *ptr) -{ - struct bufferevent_openssl *bev_ssl = ptr; - - bufferevent_incref_and_lock_(&bev_ssl->bev.bev); - if (what & EV_TIMEOUT) { - bufferevent_run_eventcb_(&bev_ssl->bev.bev, BEV_EVENT_TIMEOUT, 0); - } else - do_handshake(bev_ssl);/* XXX handle failure */ - bufferevent_decref_and_unlock_(&bev_ssl->bev.bev); + return err == SSL_ERROR_WANT_READ; } static int -set_handshake_callbacks(struct bufferevent_openssl *bev_ssl, evutil_socket_t fd) -{ - if (bev_ssl->underlying) { - bufferevent_setcb(bev_ssl->underlying, - be_openssl_handshakecb, be_openssl_handshakecb, - be_openssl_eventcb, - bev_ssl); - - if (fd < 0) - return 0; - - if (bufferevent_setfd(bev_ssl->underlying, fd)) - return 1; - - return do_handshake(bev_ssl); - } else { - struct bufferevent *bev = &bev_ssl->bev.bev; - - if (event_initialized(&bev->ev_read)) { - event_del(&bev->ev_read); - event_del(&bev->ev_write); - } - - event_assign(&bev->ev_read, bev->ev_base, fd, - EV_READ|EV_PERSIST|EV_FINALIZE, - be_openssl_handshakeeventcb, bev_ssl); - event_assign(&bev->ev_write, bev->ev_base, fd, - EV_WRITE|EV_PERSIST|EV_FINALIZE, - be_openssl_handshakeeventcb, bev_ssl); - if (fd >= 0) - bufferevent_enable(bev, bev->enabled); - return 0; - } -} - -int -bufferevent_ssl_renegotiate(struct bufferevent *bev) +SSL_is_want_write(int err) { - struct bufferevent_openssl *bev_ssl = upcast(bev); - if (!bev_ssl) - return -1; - if (SSL_renegotiate(bev_ssl->ssl) < 0) - return -1; - bev_ssl->state = BUFFEREVENT_SSL_CONNECTING; - if (set_handshake_callbacks(bev_ssl, be_openssl_auto_fd(bev_ssl, -1)) < 0) - return -1; - if (!bev_ssl->underlying) - return do_handshake(bev_ssl); - return 0; + return err == SSL_ERROR_WANT_WRITE; } static void -be_openssl_outbuf_cb(struct evbuffer *buf, - const struct evbuffer_cb_info *cbinfo, void *arg) +be_openssl_post_init(void *ssl) { - struct bufferevent_openssl *bev_ssl = arg; - int r = 0; - /* XXX need to hold a reference here. */ - - if (cbinfo->n_added && bev_ssl->state == BUFFEREVENT_SSL_OPEN) { - if (cbinfo->orig_size == 0) - r = bufferevent_add_event_(&bev_ssl->bev.bev.ev_write, - &bev_ssl->bev.bev.timeout_write); - - if (bev_ssl->underlying) - consider_writing(bev_ssl); - } - /* XXX Handle r < 0 */ - (void)r; + /* Don't explode if we decide to realloc a chunk we're writing from in + * the output buffer. */ + SSL_set_mode(ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); } - static int -be_openssl_enable(struct bufferevent *bev, short events) +openssl_read(void *ssl, unsigned char *buf, size_t len) { - struct bufferevent_openssl *bev_ssl = upcast(bev); - int r1 = 0, r2 = 0; - - if (events & EV_READ) - r1 = start_reading(bev_ssl); - if (events & EV_WRITE) - r2 = start_writing(bev_ssl); - - if (bev_ssl->underlying) { - if (events & EV_READ) - BEV_RESET_GENERIC_READ_TIMEOUT(bev); - if (events & EV_WRITE) - BEV_RESET_GENERIC_WRITE_TIMEOUT(bev); - - if (events & EV_READ) - consider_reading(bev_ssl); - if (events & EV_WRITE) - consider_writing(bev_ssl); - } - return (r1 < 0 || r2 < 0) ? -1 : 0; + return SSL_read(ssl, buf, len); } static int -be_openssl_disable(struct bufferevent *bev, short events) +openssl_write(void *ssl, const unsigned char *buf, size_t len) { - struct bufferevent_openssl *bev_ssl = upcast(bev); - - if (events & EV_READ) - stop_reading(bev_ssl); - if (events & EV_WRITE) - stop_writing(bev_ssl); - - if (bev_ssl->underlying) { - if (events & EV_READ) - BEV_DEL_GENERIC_READ_TIMEOUT(bev); - if (events & EV_WRITE) - BEV_DEL_GENERIC_WRITE_TIMEOUT(bev); - } - return 0; + return SSL_write(ssl, buf, len); } -static void -be_openssl_unlink(struct bufferevent *bev) -{ - struct bufferevent_openssl *bev_ssl = upcast(bev); - - if (bev_ssl->bev.options & BEV_OPT_CLOSE_ON_FREE) { - if (bev_ssl->underlying) { - if (BEV_UPCAST(bev_ssl->underlying)->refcnt < 2) { - event_warnx("BEV_OPT_CLOSE_ON_FREE set on an " - "bufferevent with too few references"); - } else { - bufferevent_free(bev_ssl->underlying); - /* We still have a reference to it, via our - * BIO. So we don't drop this. */ - // bev_ssl->underlying = NULL; - } - } - } else { - if (bev_ssl->underlying) { - if (bev_ssl->underlying->errorcb == be_openssl_eventcb) - bufferevent_setcb(bev_ssl->underlying, - NULL,NULL,NULL,NULL); - bufferevent_unsuspend_read_(bev_ssl->underlying, - BEV_SUSPEND_FILT_READ); - } - } -} - -static void -be_openssl_destruct(struct bufferevent *bev) +static evutil_socket_t +be_openssl_get_fd(struct bufferevent_ssl *bev_ssl) { - struct bufferevent_openssl *bev_ssl = upcast(bev); - - if (bev_ssl->bev.options & BEV_OPT_CLOSE_ON_FREE) { - if (! bev_ssl->underlying) { - evutil_socket_t fd = EVUTIL_INVALID_SOCKET; - BIO *bio = SSL_get_wbio(bev_ssl->ssl); - if (bio) - fd = BIO_get_fd(bio, NULL); - if (fd >= 0) - evutil_closesocket(fd); - } - SSL_free(bev_ssl->ssl); - } + evutil_socket_t fd = EVUTIL_INVALID_SOCKET; + BIO *bio = SSL_get_wbio(bev_ssl->ssl); + if (bio) + fd = BIO_get_fd(bio, NULL); + return fd; } static int -be_openssl_adj_timeouts(struct bufferevent *bev) +be_openssl_bio_set_fd(struct bufferevent_ssl *bev_ssl, evutil_socket_t fd) { - struct bufferevent_openssl *bev_ssl = upcast(bev); - - if (bev_ssl->underlying) { - return bufferevent_generic_adj_timeouts_(bev); + if (!bev_ssl->underlying) { + BIO *bio; + bio = BIO_new_socket((int)fd, 0); + SSL_set_bio(bev_ssl->ssl, bio, bio); } else { - return bufferevent_generic_adj_existing_timeouts_(bev); - } -} - -static int -be_openssl_flush(struct bufferevent *bufev, - short iotype, enum bufferevent_flush_mode mode) -{ - /* XXXX Implement this. */ - return 0; -} - -static int -be_openssl_set_fd(struct bufferevent_openssl *bev_ssl, - enum bufferevent_ssl_state state, evutil_socket_t fd) -{ - bev_ssl->state = state; - - switch (state) { - case BUFFEREVENT_SSL_ACCEPTING: - if (!SSL_clear(bev_ssl->ssl)) - return -1; - SSL_set_accept_state(bev_ssl->ssl); - if (set_handshake_callbacks(bev_ssl, fd) < 0) - return -1; - break; - case BUFFEREVENT_SSL_CONNECTING: - if (!SSL_clear(bev_ssl->ssl)) - return -1; - SSL_set_connect_state(bev_ssl->ssl); - if (set_handshake_callbacks(bev_ssl, fd) < 0) - return -1; - break; - case BUFFEREVENT_SSL_OPEN: - if (set_open_callbacks(bev_ssl, fd) < 0) + BIO *bio; + if (!(bio = BIO_new_bufferevent(bev_ssl->underlying))) return -1; - break; - default: - return -1; + SSL_set_bio(bev_ssl->ssl, bio, bio); } - return 0; } -static int -be_openssl_ctrl(struct bufferevent *bev, - enum bufferevent_ctrl_op op, union bufferevent_ctrl_data *data) -{ - struct bufferevent_openssl *bev_ssl = upcast(bev); - switch (op) { - case BEV_CTRL_SET_FD: - if (!bev_ssl->underlying) { - BIO *bio; - bio = BIO_new_socket((int)data->fd, 0); - SSL_set_bio(bev_ssl->ssl, bio, bio); - } else { - BIO *bio; - if (!(bio = BIO_new_bufferevent(bev_ssl->underlying))) - return -1; - SSL_set_bio(bev_ssl->ssl, bio, bio); - } - - return be_openssl_set_fd(bev_ssl, bev_ssl->old_state, data->fd); - case BEV_CTRL_GET_FD: - if (bev_ssl->underlying) { - data->fd = event_get_fd(&bev_ssl->underlying->ev_read); - } else { - data->fd = event_get_fd(&bev->ev_read); - } - return 0; - case BEV_CTRL_GET_UNDERLYING: - data->ptr = bev_ssl->underlying; - return 0; - case BEV_CTRL_CANCEL_ALL: - default: - return -1; - } -} - -SSL * -bufferevent_openssl_get_ssl(struct bufferevent *bufev) -{ - struct bufferevent_openssl *bev_ssl = upcast(bufev); - if (!bev_ssl) - return NULL; - return bev_ssl->ssl; -} - -static struct bufferevent * -bufferevent_openssl_new_impl(struct event_base *base, - struct bufferevent *underlying, - evutil_socket_t fd, - SSL *ssl, - enum bufferevent_ssl_state state, - int options) -{ - struct bufferevent_openssl *bev_ssl = NULL; - struct bufferevent_private *bev_p = NULL; - int tmp_options = options & ~BEV_OPT_THREADSAFE; - - /* Only one can be set. */ - if (underlying != NULL && fd >= 0) - goto err; - - if (!(bev_ssl = mm_calloc(1, sizeof(struct bufferevent_openssl)))) - goto err; - - bev_p = &bev_ssl->bev; - - if (bufferevent_init_common_(bev_p, base, - &bufferevent_ops_openssl, tmp_options) < 0) - goto err; - - /* Don't explode if we decide to realloc a chunk we're writing from in - * the output buffer. */ - SSL_set_mode(ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - - bev_ssl->underlying = underlying; - bev_ssl->ssl = ssl; - - bev_ssl->outbuf_cb = evbuffer_add_cb(bev_p->bev.output, - be_openssl_outbuf_cb, bev_ssl); - - if (options & BEV_OPT_THREADSAFE) - bufferevent_enable_locking_(&bev_ssl->bev.bev, NULL); - - if (underlying) { - bufferevent_init_generic_timeout_cbs_(&bev_ssl->bev.bev); - bufferevent_incref_(underlying); - } - - bev_ssl->old_state = state; - bev_ssl->last_write = -1; - - init_bio_counts(bev_ssl); - - fd = be_openssl_auto_fd(bev_ssl, fd); - if (be_openssl_set_fd(bev_ssl, state, fd)) - goto err; - - if (underlying) { - bufferevent_setwatermark(underlying, EV_READ, 0, 0); - bufferevent_enable(underlying, EV_READ|EV_WRITE); - if (state == BUFFEREVENT_SSL_OPEN) - bufferevent_suspend_read_(underlying, - BEV_SUSPEND_FILT_READ); - } - - return &bev_ssl->bev.bev; -err: - if (options & BEV_OPT_CLOSE_ON_FREE) - SSL_free(ssl); - if (bev_ssl) { - bev_ssl->ssl = NULL; - bufferevent_free(&bev_ssl->bev.bev); - } - return NULL; -} +static struct le_ssl_ops le_openssl_ops = { + SSL_init, + SSL_context_free, + (void (*)(void *))SSL_free, + (int (*)(void *))SSL_renegotiate, + openssl_write, + openssl_read, + (size_t(*)(void *))SSL_pending, + (int (*)(void *))SSL_do_handshake, + (int (*)(void *, int))SSL_get_error, + ERR_clear_error, + (int (*)(void *))SSL_clear, + (void (*)(void *))SSL_set_connect_state, + (void (*)(void *))SSL_set_accept_state, + SSL_is_ok, + SSL_is_want_read, + SSL_is_want_write, + (int (*)(void *))be_openssl_get_fd, + be_openssl_bio_set_fd, + be_openssl_post_init, + init_bio_counts, + decrement_buckets, + conn_closed, + print_err, +}; struct bufferevent * bufferevent_openssl_filter_new(struct event_base *base, @@ -1427,8 +439,8 @@ bufferevent_openssl_filter_new(struct event_base *base, SSL_set_bio(ssl, bio, bio); - bev = bufferevent_openssl_new_impl( - base, underlying, -1, ssl, state, options); + bev = bufferevent_ssl_new_impl( + base, underlying, -1, ssl, state, options, &le_openssl_ops); return bev; err: @@ -1475,8 +487,8 @@ bufferevent_openssl_socket_new(struct event_base *base, } } - return bufferevent_openssl_new_impl( - base, NULL, fd, ssl, state, options); + return bufferevent_ssl_new_impl( + base, NULL, fd, ssl, state, options, &le_openssl_ops); err: if (options & BEV_OPT_CLOSE_ON_FREE) @@ -1484,41 +496,39 @@ err: return NULL; } +int +bufferevent_ssl_renegotiate(struct bufferevent *bev) +{ + return bufferevent_ssl_renegotiate_impl(bev); +} + +SSL * +bufferevent_openssl_get_ssl(struct bufferevent *bufev) +{ + struct bufferevent_ssl *bev_ssl = bufferevent_ssl_upcast(bufev); + if (!bev_ssl) + return NULL; + return bev_ssl->ssl; +} + int bufferevent_openssl_get_allow_dirty_shutdown(struct bufferevent *bev) { - int allow_dirty_shutdown = -1; - struct bufferevent_openssl *bev_ssl; - BEV_LOCK(bev); - bev_ssl = upcast(bev); - if (bev_ssl) - allow_dirty_shutdown = bev_ssl->allow_dirty_shutdown; - BEV_UNLOCK(bev); - return allow_dirty_shutdown; + return bufferevent_ssl_get_allow_dirty_shutdown(bev); } void -bufferevent_openssl_set_allow_dirty_shutdown(struct bufferevent *bev, - int allow_dirty_shutdown) +bufferevent_openssl_set_allow_dirty_shutdown( + struct bufferevent *bev, int allow_dirty_shutdown) { - struct bufferevent_openssl *bev_ssl; - BEV_LOCK(bev); - bev_ssl = upcast(bev); - if (bev_ssl) - bev_ssl->allow_dirty_shutdown = !!allow_dirty_shutdown; - BEV_UNLOCK(bev); + bufferevent_ssl_set_allow_dirty_shutdown(bev, allow_dirty_shutdown); } unsigned long -bufferevent_get_openssl_error(struct bufferevent *bev) +bufferevent_get_openssl_error(struct bufferevent *bufev) { - unsigned long err = 0; - struct bufferevent_openssl *bev_ssl; - BEV_LOCK(bev); - bev_ssl = upcast(bev); - if (bev_ssl && bev_ssl->n_errors) { - err = bev_ssl->errors[--bev_ssl->n_errors]; - } - BEV_UNLOCK(bev); - return err; + struct bufferevent_ssl *bev_ssl = bufferevent_ssl_upcast(bufev); + if (!bev_ssl) + return -1; + return bufferevent_get_ssl_error(bufev); } diff --git a/bufferevent_ssl.c b/bufferevent_ssl.c new file mode 100644 index 00000000..be503819 --- /dev/null +++ b/bufferevent_ssl.c @@ -0,0 +1,1093 @@ +/* + * Copyright (c) 2009-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Get rid of OSX 10.7 and greater deprecation warnings. +#if defined(__APPLE__) && defined(__clang__) +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + +#include "event2/event-config.h" +#include "evconfig-private.h" + +#include + +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +#include +#include +#include +#include +#ifdef EVENT__HAVE_STDARG_H +#include +#endif +#ifdef EVENT__HAVE_UNISTD_H +#include +#endif + +#ifdef _WIN32 +#include +#endif + +#include "event2/bufferevent.h" +#include "event2/bufferevent_struct.h" +#include "event2/bufferevent_ssl.h" +#include "event2/buffer.h" +#include "event2/event.h" + +#include "mm-internal.h" +#include "bufferevent-internal.h" +#include "log-internal.h" +#include "ssl-compat.h" + +/* -------------------- + Now, here's the OpenSSL-based implementation of bufferevent. + + The implementation comes in two flavors: one that connects its SSL object + to an underlying bufferevent using a BIO_bufferevent, and one that has the + SSL object connect to a socket directly. The latter should generally be + faster, except on Windows, where your best bet is using a + bufferevent_async. + + (OpenSSL supports many other BIO types, too. But we can't use any unless + we have a good way to get notified when they become readable/writable.) + -------------------- */ + + +static int be_ssl_enable(struct bufferevent *, short); +static int be_ssl_disable(struct bufferevent *, short); +static void be_ssl_unlink(struct bufferevent *); +static void be_ssl_destruct(struct bufferevent *); +static int be_ssl_adj_timeouts(struct bufferevent *); +static int be_ssl_flush(struct bufferevent *bufev, + short iotype, enum bufferevent_flush_mode mode); +static int be_ssl_ctrl(struct bufferevent *, enum bufferevent_ctrl_op, union bufferevent_ctrl_data *); + +const struct bufferevent_ops bufferevent_ops_ssl = { + "ssl", + evutil_offsetof(struct bufferevent_ssl, bev.bev), + be_ssl_enable, + be_ssl_disable, + be_ssl_unlink, + be_ssl_destruct, + be_ssl_adj_timeouts, + be_ssl_flush, + be_ssl_ctrl, +}; + +/* Given a bufferevent, return a pointer to the bufferevent_ssl that + * contains it, if any. */ +struct bufferevent_ssl * +bufferevent_ssl_upcast(struct bufferevent *bev) +{ + struct bufferevent_ssl *bev_o; + if (!BEV_IS_SSL(bev)) + return NULL; + bev_o = (void*)( ((char*)bev) - + evutil_offsetof(struct bufferevent_ssl, bev.bev)); + EVUTIL_ASSERT(BEV_IS_SSL(&bev_o->bev.bev)); + return bev_o; +} + +void +bufferevent_ssl_put_error(struct bufferevent_ssl *bev_ssl, unsigned long err) +{ + if (bev_ssl->n_errors == NUM_ERRORS) + return; + /* The error type according to openssl is "unsigned long", but + openssl never uses more than 32 bits of it. It _can't_ use more + than 32 bits of it, since it needs to report errors on systems + where long is only 32 bits. + */ + bev_ssl->errors[bev_ssl->n_errors++] = (ev_uint32_t) err; +} + +/* Have the base communications channel (either the underlying bufferevent or + * ev_read and ev_write) start reading. Take the read-blocked-on-write flag + * into account. */ +static int +start_reading(struct bufferevent_ssl *bev_ssl) +{ + if (bev_ssl->underlying) { + bufferevent_unsuspend_read_(bev_ssl->underlying, + BEV_SUSPEND_FILT_READ); + return 0; + } else { + struct bufferevent *bev = &bev_ssl->bev.bev; + int r; + r = bufferevent_add_event_(&bev->ev_read, &bev->timeout_read); + if (r == 0 && bev_ssl->read_blocked_on_write) + r = bufferevent_add_event_(&bev->ev_write, + &bev->timeout_write); + return r; + } +} + +/* Have the base communications channel (either the underlying bufferevent or + * ev_read and ev_write) start writing. Take the write-blocked-on-read flag + * into account. */ +static int +start_writing(struct bufferevent_ssl *bev_ssl) +{ + int r = 0; + if (bev_ssl->underlying) { + if (bev_ssl->write_blocked_on_read) { + bufferevent_unsuspend_read_(bev_ssl->underlying, + BEV_SUSPEND_FILT_READ); + } + } else { + struct bufferevent *bev = &bev_ssl->bev.bev; + r = bufferevent_add_event_(&bev->ev_write, &bev->timeout_write); + if (!r && bev_ssl->write_blocked_on_read) + r = bufferevent_add_event_(&bev->ev_read, + &bev->timeout_read); + } + return r; +} + +void +bufferevent_ssl_stop_reading(struct bufferevent_ssl *bev_ssl) +{ + if (bev_ssl->write_blocked_on_read) + return; + if (bev_ssl->underlying) { + bufferevent_suspend_read_(bev_ssl->underlying, + BEV_SUSPEND_FILT_READ); + } else { + struct bufferevent *bev = &bev_ssl->bev.bev; + event_del(&bev->ev_read); + } +} + +void +bufferevent_ssl_stop_writing(struct bufferevent_ssl *bev_ssl) +{ + if (bev_ssl->read_blocked_on_write) + return; + if (bev_ssl->underlying) { + bufferevent_unsuspend_read_(bev_ssl->underlying, + BEV_SUSPEND_FILT_READ); + } else { + struct bufferevent *bev = &bev_ssl->bev.bev; + event_del(&bev->ev_write); + } +} + +static int +set_rbow(struct bufferevent_ssl *bev_ssl) +{ + if (!bev_ssl->underlying) + bufferevent_ssl_stop_reading(bev_ssl); + bev_ssl->read_blocked_on_write = 1; + return start_writing(bev_ssl); +} + +static int +set_wbor(struct bufferevent_ssl *bev_ssl) +{ + if (!bev_ssl->underlying) + bufferevent_ssl_stop_writing(bev_ssl); + bev_ssl->write_blocked_on_read = 1; + return start_reading(bev_ssl); +} + +static int +clear_rbow(struct bufferevent_ssl *bev_ssl) +{ + struct bufferevent *bev = &bev_ssl->bev.bev; + int r = 0; + bev_ssl->read_blocked_on_write = 0; + if (!(bev->enabled & EV_WRITE)) + bufferevent_ssl_stop_writing(bev_ssl); + if (bev->enabled & EV_READ) + r = start_reading(bev_ssl); + return r; +} + + +static int +clear_wbor(struct bufferevent_ssl *bev_ssl) +{ + struct bufferevent *bev = &bev_ssl->bev.bev; + int r = 0; + bev_ssl->write_blocked_on_read = 0; + if (!(bev->enabled & EV_READ)) + bufferevent_ssl_stop_reading(bev_ssl); + if (bev->enabled & EV_WRITE) + r = start_writing(bev_ssl); + return r; +} + +#define OP_MADE_PROGRESS 1 +#define OP_BLOCKED 2 +#define OP_ERR 4 + +/* Return a bitmask of OP_MADE_PROGRESS (if we read anything); OP_BLOCKED (if + we're now blocked); and OP_ERR (if an error occurred). */ +static int +do_read(struct bufferevent_ssl *bev_ssl, int n_to_read) { + /* Requires lock */ + struct bufferevent *bev = &bev_ssl->bev.bev; + struct evbuffer *input = bev->input; + int r, n, i, n_used = 0, atmost; + struct evbuffer_iovec space[2]; + int result = 0; + + if (bev_ssl->bev.read_suspended) + return 0; + + atmost = bufferevent_get_read_max_(&bev_ssl->bev); + if (n_to_read > atmost) + n_to_read = atmost; + + n = evbuffer_reserve_space(input, n_to_read, space, 2); + if (n < 0) + return OP_ERR; + + for (i=0; ibev.read_suspended) + break; + bev_ssl->ssl_ops->clear_error(); + r = bev_ssl->ssl_ops->read(bev_ssl->ssl, space[i].iov_base, space[i].iov_len); + if (r>0) { + result |= OP_MADE_PROGRESS; + if (bev_ssl->read_blocked_on_write) + if (clear_rbow(bev_ssl) < 0) + return OP_ERR | result; + ++n_used; + space[i].iov_len = r; + bev_ssl->ssl_ops->decrement_buckets(bev_ssl); + } else { + int err = bev_ssl->ssl_ops->get_error(bev_ssl->ssl, r); + bev_ssl->ssl_ops->print_err(err); + if (bev_ssl->ssl_ops->err_is_want_read(err)) { + /* Can't read until underlying has more data. */ + if (bev_ssl->read_blocked_on_write) + if (clear_rbow(bev_ssl) < 0) + return OP_ERR | result; + } else if(bev_ssl->ssl_ops->err_is_want_write(err)) { + /* This read operation requires a write, and the + * underlying is full */ + if (!bev_ssl->read_blocked_on_write) + if (set_rbow(bev_ssl) < 0) + return OP_ERR | result; + } else { + bev_ssl->ssl_ops->conn_closed(bev_ssl, BEV_EVENT_READING, err, r); + } + result |= OP_BLOCKED; + break; /* out of the loop */ + } + } + + if (n_used) { + evbuffer_commit_space(input, space, n_used); + if (bev_ssl->underlying) + BEV_RESET_GENERIC_READ_TIMEOUT(bev); + } + + return result; +} + +/* Return a bitmask of OP_MADE_PROGRESS (if we wrote anything); OP_BLOCKED (if + we're now blocked); and OP_ERR (if an error occurred). */ +static int +do_write(struct bufferevent_ssl *bev_ssl, int atmost) +{ + int i, r, n, n_written = 0; + struct bufferevent *bev = &bev_ssl->bev.bev; + struct evbuffer *output = bev->output; + struct evbuffer_iovec space[8]; + int result = 0; + + if (bev_ssl->last_write > 0) + atmost = bev_ssl->last_write; + else + atmost = bufferevent_get_write_max_(&bev_ssl->bev); + + n = evbuffer_peek(output, atmost, NULL, space, 8); + if (n < 0) + return OP_ERR | result; + + if (n > 8) + n = 8; + for (i=0; i < n; ++i) { + if (bev_ssl->bev.write_suspended) + break; + + /* SSL_write will (reasonably) return 0 if we tell it to + send 0 data. Skip this case so we don't interpret the + result as an error */ + if (space[i].iov_len == 0) + continue; + + bev_ssl->ssl_ops->clear_error(); + r = bev_ssl->ssl_ops->write(bev_ssl->ssl, space[i].iov_base, + space[i].iov_len); + if (r > 0) { + result |= OP_MADE_PROGRESS; + if (bev_ssl->write_blocked_on_read) + if (clear_wbor(bev_ssl) < 0) + return OP_ERR | result; + n_written += r; + bev_ssl->last_write = -1; + bev_ssl->ssl_ops->decrement_buckets(bev_ssl); + } else { + int err = bev_ssl->ssl_ops->get_error(bev_ssl->ssl, r); + bev_ssl->ssl_ops->print_err(err); + if (bev_ssl->ssl_ops->err_is_want_write(err)) { + /* Can't read until underlying has more data. */ + if (bev_ssl->write_blocked_on_read) + if (clear_wbor(bev_ssl) < 0) + return OP_ERR | result; + bev_ssl->last_write = space[i].iov_len; + } else if (bev_ssl->ssl_ops->err_is_want_read(err)) { + /* This read operation requires a write, and the + * underlying is full */ + if (!bev_ssl->write_blocked_on_read) + if (set_wbor(bev_ssl) < 0) + return OP_ERR | result; + bev_ssl->last_write = space[i].iov_len; + } else { + bev_ssl->ssl_ops->conn_closed(bev_ssl, BEV_EVENT_WRITING, err, r); + bev_ssl->last_write = -1; + } + result |= OP_BLOCKED; + break; + } + } + if (n_written) { + evbuffer_drain(output, n_written); + if (bev_ssl->underlying) + BEV_RESET_GENERIC_WRITE_TIMEOUT(bev); + + bufferevent_trigger_nolock_(bev, EV_WRITE, BEV_OPT_DEFER_CALLBACKS); + } + return result; +} + +#define WRITE_FRAME 15000 + +#define READ_DEFAULT 4096 + +/* Try to figure out how many bytes to read; return 0 if we shouldn't be + * reading. */ +static int +bytes_to_read(struct bufferevent_ssl *bev) +{ + struct evbuffer *input = bev->bev.bev.input; + struct event_watermark *wm = &bev->bev.bev.wm_read; + int result = READ_DEFAULT; + ev_ssize_t limit; + /* XXX 99% of this is generic code that nearly all bufferevents will + * want. */ + + if (bev->write_blocked_on_read) { + return 0; + } + + if (! (bev->bev.bev.enabled & EV_READ)) { + return 0; + } + + if (bev->bev.read_suspended) { + return 0; + } + + if (wm->high) { + if (evbuffer_get_length(input) >= wm->high) { + return 0; + } + + result = wm->high - evbuffer_get_length(input); + } else { + result = READ_DEFAULT; + } + + /* Respect the rate limit */ + limit = bufferevent_get_read_max_(&bev->bev); + if (result > limit) { + result = limit; + } + + return result; +} + + +/* Things look readable. If write is blocked on read, write till it isn't. + * Read from the underlying buffer until we block or we hit our high-water + * mark. + */ +static void +consider_reading(struct bufferevent_ssl *bev_ssl) +{ + int r; + int n_to_read; + int all_result_flags = 0; + + while (bev_ssl->write_blocked_on_read) { + r = do_write(bev_ssl, WRITE_FRAME); + if (r & (OP_BLOCKED|OP_ERR)) + break; + } + if (bev_ssl->write_blocked_on_read) + return; + + n_to_read = bytes_to_read(bev_ssl); + + while (n_to_read) { + r = do_read(bev_ssl, n_to_read); + all_result_flags |= r; + + if (r & (OP_BLOCKED|OP_ERR)) + break; + + if (bev_ssl->bev.read_suspended) + break; + + /* Read all pending data. This won't hit the network + * again, and will (most importantly) put us in a state + * where we don't need to read anything else until the + * socket is readable again. It'll potentially make us + * overrun our read high-watermark (somewhat + * regrettable). The damage to the rate-limit has + * already been done, since OpenSSL went and read a + * whole SSL record anyway. */ + n_to_read = bev_ssl->ssl_ops->pending(bev_ssl->ssl); + + /* XXX This if statement is actually a bad bug, added to avoid + * XXX a worse bug. + * + * The bad bug: It can potentially cause resource unfairness + * by reading too much data from the underlying bufferevent; + * it can potentially cause read looping if the underlying + * bufferevent is a bufferevent_pair and deferred callbacks + * aren't used. + * + * The worse bug: If we didn't do this, then we would + * potentially not read any more from bev_ssl->underlying + * until more data arrived there, which could lead to us + * waiting forever. + */ + if (!n_to_read && bev_ssl->underlying) + n_to_read = bytes_to_read(bev_ssl); + } + + if (all_result_flags & OP_MADE_PROGRESS) { + struct bufferevent *bev = &bev_ssl->bev.bev; + + bufferevent_trigger_nolock_(bev, EV_READ, 0); + } + + if (!bev_ssl->underlying) { + /* Should be redundant, but let's avoid busy-looping */ + if (bev_ssl->bev.read_suspended || + !(bev_ssl->bev.bev.enabled & EV_READ)) { + event_del(&bev_ssl->bev.bev.ev_read); + } + } +} + +static void +consider_writing(struct bufferevent_ssl *bev_ssl) +{ + int r; + struct evbuffer *output = bev_ssl->bev.bev.output; + struct evbuffer *target = NULL; + struct event_watermark *wm = NULL; + + while (bev_ssl->read_blocked_on_write) { + r = do_read(bev_ssl, 1024); /* XXXX 1024 is a hack */ + if (r & OP_MADE_PROGRESS) { + struct bufferevent *bev = &bev_ssl->bev.bev; + + bufferevent_trigger_nolock_(bev, EV_READ, 0); + } + if (r & (OP_ERR|OP_BLOCKED)) + break; + } + if (bev_ssl->read_blocked_on_write) + return; + if (bev_ssl->underlying) { + target = bev_ssl->underlying->output; + wm = &bev_ssl->underlying->wm_write; + } + while ((bev_ssl->bev.bev.enabled & EV_WRITE) && + (! bev_ssl->bev.write_suspended) && + evbuffer_get_length(output) && + (!target || (! wm->high || evbuffer_get_length(target) < wm->high))) { + int n_to_write; + if (wm && wm->high) + n_to_write = wm->high - evbuffer_get_length(target); + else + n_to_write = WRITE_FRAME; + r = do_write(bev_ssl, n_to_write); + if (r & (OP_BLOCKED|OP_ERR)) + break; + } + + if (!bev_ssl->underlying) { + if (evbuffer_get_length(output) == 0) { + event_del(&bev_ssl->bev.bev.ev_write); + } else if (bev_ssl->bev.write_suspended || + !(bev_ssl->bev.bev.enabled & EV_WRITE)) { + /* Should be redundant, but let's avoid busy-looping */ + event_del(&bev_ssl->bev.bev.ev_write); + } + } +} + +static void +be_ssl_readcb(struct bufferevent *bev_base, void *ctx) +{ + struct bufferevent_ssl *bev_ssl = ctx; + consider_reading(bev_ssl); +} + +static void +be_ssl_writecb(struct bufferevent *bev_base, void *ctx) +{ + struct bufferevent_ssl *bev_ssl = ctx; + consider_writing(bev_ssl); +} + +static void +be_ssl_eventcb(struct bufferevent *bev_base, short what, void *ctx) +{ + struct bufferevent_ssl *bev_ssl = ctx; + int event = 0; + + if (what & BEV_EVENT_EOF) { + if (bev_ssl->allow_dirty_shutdown) + event = BEV_EVENT_EOF; + else + event = BEV_EVENT_ERROR; + } else if (what & BEV_EVENT_TIMEOUT) { + /* We sure didn't set this. Propagate it to the user. */ + event = what; + } else if (what & BEV_EVENT_ERROR) { + /* An error occurred on the connection. Propagate it to the user. */ + event = what; + } else if (what & BEV_EVENT_CONNECTED) { + /* Ignore it. We're saying SSL_connect() already, which will + eat it. */ + } + if (event) + bufferevent_run_eventcb_(&bev_ssl->bev.bev, event, 0); +} + +static void +be_ssl_readeventcb(evutil_socket_t fd, short what, void *ptr) +{ + struct bufferevent_ssl *bev_ssl = ptr; + bufferevent_incref_and_lock_(&bev_ssl->bev.bev); + if (what == EV_TIMEOUT) { + bufferevent_run_eventcb_(&bev_ssl->bev.bev, + BEV_EVENT_TIMEOUT|BEV_EVENT_READING, 0); + } else { + consider_reading(bev_ssl); + } + bufferevent_decref_and_unlock_(&bev_ssl->bev.bev); +} + +static void +be_ssl_writeeventcb(evutil_socket_t fd, short what, void *ptr) +{ + struct bufferevent_ssl *bev_ssl = ptr; + bufferevent_incref_and_lock_(&bev_ssl->bev.bev); + if (what == EV_TIMEOUT) { + bufferevent_run_eventcb_(&bev_ssl->bev.bev, + BEV_EVENT_TIMEOUT|BEV_EVENT_WRITING, 0); + } else { + consider_writing(bev_ssl); + } + bufferevent_decref_and_unlock_(&bev_ssl->bev.bev); +} + +static evutil_socket_t +be_ssl_auto_fd(struct bufferevent_ssl *bev_ssl, evutil_socket_t fd) +{ + if (!bev_ssl->underlying) { + struct bufferevent *bev = &bev_ssl->bev.bev; + if (event_initialized(&bev->ev_read) && fd < 0) { + fd = event_get_fd(&bev->ev_read); + } + } + return fd; +} + +static int +set_open_callbacks(struct bufferevent_ssl *bev_ssl, evutil_socket_t fd) +{ + if (bev_ssl->underlying) { + bufferevent_setcb(bev_ssl->underlying, + be_ssl_readcb, be_ssl_writecb, be_ssl_eventcb, + bev_ssl); + return 0; + } else { + struct bufferevent *bev = &bev_ssl->bev.bev; + int rpending=0, wpending=0, r1=0, r2=0; + + if (event_initialized(&bev->ev_read)) { + rpending = event_pending(&bev->ev_read, EV_READ, NULL); + wpending = event_pending(&bev->ev_write, EV_WRITE, NULL); + + event_del(&bev->ev_read); + event_del(&bev->ev_write); + } + + event_assign(&bev->ev_read, bev->ev_base, fd, + EV_READ|EV_PERSIST|EV_FINALIZE, + be_ssl_readeventcb, bev_ssl); + event_assign(&bev->ev_write, bev->ev_base, fd, + EV_WRITE|EV_PERSIST|EV_FINALIZE, + be_ssl_writeeventcb, bev_ssl); + + if (rpending) + r1 = bufferevent_add_event_(&bev->ev_read, &bev->timeout_read); + if (wpending) + r2 = bufferevent_add_event_(&bev->ev_write, &bev->timeout_write); + + return (r1<0 || r2<0) ? -1 : 0; + } +} + +static int +do_handshake(struct bufferevent_ssl *bev_ssl) +{ + int r; + + switch (bev_ssl->state) { + default: + case BUFFEREVENT_SSL_OPEN: + EVUTIL_ASSERT(0); + return -1; + case BUFFEREVENT_SSL_CONNECTING: + case BUFFEREVENT_SSL_ACCEPTING: + bev_ssl->ssl_ops->clear_error(); + r = bev_ssl->ssl_ops->handshake(bev_ssl->ssl); + break; + } + bev_ssl->ssl_ops->decrement_buckets(bev_ssl); + + if (bev_ssl->ssl_ops->err_is_ok(r)) { + evutil_socket_t fd = event_get_fd(&bev_ssl->bev.bev.ev_read); + /* We're done! */ + bev_ssl->state = BUFFEREVENT_SSL_OPEN; + set_open_callbacks(bev_ssl, fd); /* XXXX handle failure */ + /* Call do_read and do_write as needed */ + bufferevent_enable(&bev_ssl->bev.bev, bev_ssl->bev.bev.enabled); + bufferevent_run_eventcb_(&bev_ssl->bev.bev, + BEV_EVENT_CONNECTED, 0); + return 1; + } else { + int err = bev_ssl->ssl_ops->get_error(bev_ssl->ssl, r); + bev_ssl->ssl_ops->print_err(err); + if (bev_ssl->ssl_ops->err_is_want_write(err)) { + bufferevent_ssl_stop_reading(bev_ssl); + return start_writing(bev_ssl); + } else if (bev_ssl->ssl_ops->err_is_want_read(err)) { + bufferevent_ssl_stop_writing(bev_ssl); + return start_reading(bev_ssl); + } else { + bev_ssl->ssl_ops->conn_closed(bev_ssl, BEV_EVENT_READING, err, r); + return -1; + } + } +} + +static void +be_ssl_handshakecb(struct bufferevent *bev_base, void *ctx) +{ + struct bufferevent_ssl *bev_ssl = ctx; + do_handshake(bev_ssl);/* XXX handle failure */ +} + +static void +be_ssl_handshakeeventcb(evutil_socket_t fd, short what, void *ptr) +{ + struct bufferevent_ssl *bev_ssl = ptr; + + bufferevent_incref_and_lock_(&bev_ssl->bev.bev); + if (what & EV_TIMEOUT) { + bufferevent_run_eventcb_(&bev_ssl->bev.bev, BEV_EVENT_TIMEOUT, 0); + } else + do_handshake(bev_ssl);/* XXX handle failure */ + bufferevent_decref_and_unlock_(&bev_ssl->bev.bev); +} + +static int +set_handshake_callbacks(struct bufferevent_ssl *bev_ssl, evutil_socket_t fd) +{ + if (bev_ssl->underlying) { + bufferevent_setcb(bev_ssl->underlying, + be_ssl_handshakecb, be_ssl_handshakecb, + be_ssl_eventcb, + bev_ssl); + + if (fd < 0) + return 0; + + if (bufferevent_setfd(bev_ssl->underlying, fd)) + return 1; + + return do_handshake(bev_ssl); + } else { + struct bufferevent *bev = &bev_ssl->bev.bev; + + if (event_initialized(&bev->ev_read)) { + event_del(&bev->ev_read); + event_del(&bev->ev_write); + } + + event_assign(&bev->ev_read, bev->ev_base, fd, + EV_READ|EV_PERSIST|EV_FINALIZE, + be_ssl_handshakeeventcb, bev_ssl); + event_assign(&bev->ev_write, bev->ev_base, fd, + EV_WRITE|EV_PERSIST|EV_FINALIZE, + be_ssl_handshakeeventcb, bev_ssl); + if (fd >= 0) + bufferevent_enable(bev, bev->enabled); + return 0; + } +} + +int +bufferevent_ssl_renegotiate_impl(struct bufferevent *bev) +{ + struct bufferevent_ssl *bev_ssl = bufferevent_ssl_upcast(bev); + if (!bev_ssl) + return -1; + if (bev_ssl->ssl_ops->renegotiate(bev_ssl->ssl) < 0) + return -1; + bev_ssl->state = BUFFEREVENT_SSL_CONNECTING; + if (set_handshake_callbacks(bev_ssl, be_ssl_auto_fd(bev_ssl, -1)) < 0) + return -1; + if (!bev_ssl->underlying) + return do_handshake(bev_ssl); + return 0; +} + +static void +be_ssl_outbuf_cb(struct evbuffer *buf, + const struct evbuffer_cb_info *cbinfo, void *arg) +{ + struct bufferevent_ssl *bev_ssl = arg; + int r = 0; + /* XXX need to hold a reference here. */ + + if (cbinfo->n_added && bev_ssl->state == BUFFEREVENT_SSL_OPEN) { + if (cbinfo->orig_size == 0) + r = bufferevent_add_event_(&bev_ssl->bev.bev.ev_write, + &bev_ssl->bev.bev.timeout_write); + + if (bev_ssl->underlying) + consider_writing(bev_ssl); + } + /* XXX Handle r < 0 */ + (void)r; +} + + +static int +be_ssl_enable(struct bufferevent *bev, short events) +{ + struct bufferevent_ssl *bev_ssl = bufferevent_ssl_upcast(bev); + int r1 = 0, r2 = 0; + + if (events & EV_READ) + r1 = start_reading(bev_ssl); + if (events & EV_WRITE) + r2 = start_writing(bev_ssl); + + if (bev_ssl->underlying) { + if (events & EV_READ) + BEV_RESET_GENERIC_READ_TIMEOUT(bev); + if (events & EV_WRITE) + BEV_RESET_GENERIC_WRITE_TIMEOUT(bev); + + if (events & EV_READ) + consider_reading(bev_ssl); + if (events & EV_WRITE) + consider_writing(bev_ssl); + } + return (r1 < 0 || r2 < 0) ? -1 : 0; +} + +static int +be_ssl_disable(struct bufferevent *bev, short events) +{ + struct bufferevent_ssl *bev_ssl = bufferevent_ssl_upcast(bev); + + if (events & EV_READ) + bufferevent_ssl_stop_reading(bev_ssl); + if (events & EV_WRITE) + bufferevent_ssl_stop_writing(bev_ssl); + + if (bev_ssl->underlying) { + if (events & EV_READ) + BEV_DEL_GENERIC_READ_TIMEOUT(bev); + if (events & EV_WRITE) + BEV_DEL_GENERIC_WRITE_TIMEOUT(bev); + } + return 0; +} + +static void +be_ssl_unlink(struct bufferevent *bev) +{ + struct bufferevent_ssl *bev_ssl = bufferevent_ssl_upcast(bev); + + if (bev_ssl->bev.options & BEV_OPT_CLOSE_ON_FREE) { + if (bev_ssl->underlying) { + if (BEV_UPCAST(bev_ssl->underlying)->refcnt < 2) { + event_warnx("BEV_OPT_CLOSE_ON_FREE set on an " + "bufferevent with too few references"); + } else { + bufferevent_free(bev_ssl->underlying); + /* We still have a reference to it, via our + * BIO. So we don't drop this. */ + // bev_ssl->underlying = NULL; + } + } + } else { + if (bev_ssl->underlying) { + if (bev_ssl->underlying->errorcb == be_ssl_eventcb) + bufferevent_setcb(bev_ssl->underlying, + NULL,NULL,NULL,NULL); + bufferevent_unsuspend_read_(bev_ssl->underlying, + BEV_SUSPEND_FILT_READ); + } + } +} + +static void +be_ssl_destruct(struct bufferevent *bev) +{ + struct bufferevent_ssl *bev_ssl = bufferevent_ssl_upcast(bev); + + if (bev_ssl->bev.options & BEV_OPT_CLOSE_ON_FREE) { + if (! bev_ssl->underlying) { + evutil_socket_t fd = bev_ssl->ssl_ops->get_fd(bev_ssl); + if (fd >= 0) + evutil_closesocket(fd); + } + } + bev_ssl->ssl_ops->free(bev_ssl->ssl, bev_ssl->bev.options); +} + +static int +be_ssl_adj_timeouts(struct bufferevent *bev) +{ + struct bufferevent_ssl *bev_ssl = bufferevent_ssl_upcast(bev); + + if (bev_ssl->underlying) { + return bufferevent_generic_adj_timeouts_(bev); + } else { + return bufferevent_generic_adj_existing_timeouts_(bev); + } +} + +static int +be_ssl_flush(struct bufferevent *bufev, + short iotype, enum bufferevent_flush_mode mode) +{ + /* XXXX Implement this. */ + return 0; +} + +static int +be_ssl_set_fd(struct bufferevent_ssl *bev_ssl, + enum bufferevent_ssl_state state, evutil_socket_t fd) +{ + bev_ssl->state = state; + + switch (state) { + case BUFFEREVENT_SSL_ACCEPTING: + if (!bev_ssl->ssl_ops->clear(bev_ssl->ssl)) + return -1; + bev_ssl->ssl_ops->set_accept_state(bev_ssl->ssl); + if (set_handshake_callbacks(bev_ssl, fd) < 0) + return -1; + break; + case BUFFEREVENT_SSL_CONNECTING: + if (!bev_ssl->ssl_ops->clear(bev_ssl->ssl)) + return -1; + bev_ssl->ssl_ops->set_connect_state(bev_ssl->ssl); + if (set_handshake_callbacks(bev_ssl, fd) < 0) + return -1; + break; + case BUFFEREVENT_SSL_OPEN: + if (set_open_callbacks(bev_ssl, fd) < 0) + return -1; + break; + default: + return -1; + } + + return 0; +} + +static int +be_ssl_ctrl(struct bufferevent *bev, + enum bufferevent_ctrl_op op, union bufferevent_ctrl_data *data) +{ + int ret = 0; + struct bufferevent_ssl *bev_ssl = bufferevent_ssl_upcast(bev); + switch (op) { + case BEV_CTRL_SET_FD: + if ((ret = bev_ssl->ssl_ops->bio_set_fd(bev_ssl, data->fd)) != 0) + return ret; + return be_ssl_set_fd(bev_ssl, bev_ssl->old_state, data->fd); + case BEV_CTRL_GET_FD: + if (bev_ssl->underlying) { + data->fd = event_get_fd(&bev_ssl->underlying->ev_read); + } else { + data->fd = event_get_fd(&bev->ev_read); + } + return 0; + case BEV_CTRL_GET_UNDERLYING: + data->ptr = bev_ssl->underlying; + return 0; + case BEV_CTRL_CANCEL_ALL: + default: + return -1; + } +} + +struct bufferevent * +bufferevent_ssl_new_impl(struct event_base *base, + struct bufferevent *underlying, + evutil_socket_t fd, + void *ssl, + enum bufferevent_ssl_state state, + int options, + struct le_ssl_ops *ssl_ops) +{ + struct bufferevent_ssl *bev_ssl = NULL; + struct bufferevent_private *bev_p = NULL; + int tmp_options = options & ~BEV_OPT_THREADSAFE; + + /* Only one can be set. */ + if (underlying != NULL && fd >= 0) + goto err; + + if (!(bev_ssl = mm_calloc(1, sizeof(struct bufferevent_ssl)))) + goto err; + + bev_p = &bev_ssl->bev; + + if (bufferevent_init_common_(bev_p, base, + &bufferevent_ops_ssl, tmp_options) < 0) + goto err; + + bev_ssl->ssl_ops = ssl_ops; + + bev_ssl->ssl = bev_ssl->ssl_ops->init(ssl); + bev_ssl->ssl_ops->post_init(ssl); + + bev_ssl->underlying = underlying; + + bev_ssl->outbuf_cb = evbuffer_add_cb(bev_p->bev.output, + be_ssl_outbuf_cb, bev_ssl); + + if (options & BEV_OPT_THREADSAFE) + bufferevent_enable_locking_(&bev_ssl->bev.bev, NULL); + + if (underlying) { + bufferevent_init_generic_timeout_cbs_(&bev_ssl->bev.bev); + bufferevent_incref_(underlying); + } + + bev_ssl->old_state = state; + bev_ssl->last_write = -1; + + bev_ssl->ssl_ops->init_bio_counts(bev_ssl); + + fd = be_ssl_auto_fd(bev_ssl, fd); + if (be_ssl_set_fd(bev_ssl, state, fd)) + goto err; + + if (underlying) { + bufferevent_setwatermark(underlying, EV_READ, 0, 0); + bufferevent_enable(underlying, EV_READ|EV_WRITE); + if (state == BUFFEREVENT_SSL_OPEN) + bufferevent_suspend_read_(underlying, + BEV_SUSPEND_FILT_READ); + } + + return &bev_ssl->bev.bev; +err: + if (bev_ssl) { + if (bev_ssl->ssl && (options & BEV_OPT_CLOSE_ON_FREE)) + bev_ssl->ssl_ops->free(bev_ssl->ssl, options); + bev_ssl->ssl = NULL; + bufferevent_free(&bev_ssl->bev.bev); + } else { + if (ssl && (options & BEV_OPT_CLOSE_ON_FREE)) + bev_ssl->ssl_ops->free_raw(bev_ssl->ssl); + } + return NULL; +} + +int +bufferevent_ssl_get_allow_dirty_shutdown(struct bufferevent *bev) +{ + int allow_dirty_shutdown = -1; + struct bufferevent_ssl *bev_ssl; + BEV_LOCK(bev); + bev_ssl = bufferevent_ssl_upcast(bev); + if (bev_ssl) + allow_dirty_shutdown = bev_ssl->allow_dirty_shutdown; + BEV_UNLOCK(bev); + return allow_dirty_shutdown; +} + +void +bufferevent_ssl_set_allow_dirty_shutdown(struct bufferevent *bev, + int allow_dirty_shutdown) +{ + struct bufferevent_ssl *bev_ssl; + BEV_LOCK(bev); + bev_ssl = bufferevent_ssl_upcast(bev); + if (bev_ssl) + bev_ssl->allow_dirty_shutdown = !!allow_dirty_shutdown; + BEV_UNLOCK(bev); +} + +unsigned long +bufferevent_get_ssl_error(struct bufferevent *bev) +{ + unsigned long err = 0; + struct bufferevent_ssl *bev_ssl; + BEV_LOCK(bev); + bev_ssl = bufferevent_ssl_upcast(bev); + if (bev_ssl && bev_ssl->n_errors) { + err = bev_ssl->errors[--bev_ssl->n_errors]; + } + BEV_UNLOCK(bev); + return err; +} diff --git a/ssl-compat.h b/ssl-compat.h new file mode 100644 index 00000000..ee88b624 --- /dev/null +++ b/ssl-compat.h @@ -0,0 +1,102 @@ +#ifndef SSL_COMPACT_H +#define SSL_COMPACT_H + +#include "event.h" +#include "bufferevent-internal.h" +#include "event2/bufferevent_ssl.h" +struct bufferevent_ssl; + +struct le_ssl_ops { + void *(*init)(void *ssl); + void (*free)(void *ssl, int flags); + void (*free_raw)(void *ssl); + int (*renegotiate)(void *ssl); + int (*write)(void *ssl, const unsigned char *buf, size_t len); + int (*read)(void *ssl, unsigned char *buf, size_t len); + size_t (*pending)(void *ssl); + int (*handshake)(void *ssl); + int (*get_error)(void *ssl, int ret); + void (*clear_error)(void); + int (*clear)(void *ssl); + void (*set_connect_state)(void *ssl); + void (*set_accept_state)(void *ssl); + int (*err_is_ok)(int err); + int (*err_is_want_read)(int err); + int (*err_is_want_write)(int err); + evutil_socket_t (*get_fd)(void *ssl); + int (*bio_set_fd)(struct bufferevent_ssl *ssl, evutil_socket_t fd); + void (*post_init)(void *ssl); + void (*init_bio_counts)(struct bufferevent_ssl *bev); + void (*decrement_buckets)(struct bufferevent_ssl *bev); + void (*conn_closed)( + struct bufferevent_ssl *bev, int when, int errcode, int ret); + void (*print_err)(int err); +}; + +struct bio_data_counts { + unsigned long n_written; + unsigned long n_read; +}; + +struct bufferevent_ssl { + /* Shared fields with common bufferevent implementation code. + If we were set up with an underlying bufferevent, we use the + events here as timers only. If we have an SSL, then we use + the events as socket events. + */ + struct bufferevent_private bev; + /* An underlying bufferevent that we're directing our output to. + If it's NULL, then we're connected to an fd, not an evbuffer. */ + struct bufferevent *underlying; + /* The SSL context doing our encryption. */ + void *ssl; + /* The SSL operations doing on ssl. */ + struct le_ssl_ops *ssl_ops; + + /* A callback that's invoked when data arrives on our outbuf so we + know to write data to the SSL. */ + struct evbuffer_cb_entry *outbuf_cb; + + /* A count of how much data the bios have read/written total. Used + for rate-limiting. */ + struct bio_data_counts counts; + + /* If this value is greater than 0, then the last SSL_write blocked, + * and we need to try it again with this many bytes. */ + ev_ssize_t last_write; + +#define NUM_ERRORS 3 + ev_uint32_t errors[NUM_ERRORS]; + + /* When we next get available space, we should say "read" instead of + "write". This can happen if there's a renegotiation during a read + operation. */ + unsigned read_blocked_on_write : 1; + /* When we next get data, we should say "write" instead of "read". */ + unsigned write_blocked_on_read : 1; + /* Treat TCP close before SSL close on SSL >= v3 as clean EOF. */ + unsigned allow_dirty_shutdown : 1; + /* XXX */ + unsigned n_errors : 2; + + /* Are we currently connecting, accepting, or doing IO? */ + unsigned state : 2; + /* If we reset fd, we sould reset state too */ + unsigned old_state : 2; +}; + +struct bufferevent *bufferevent_ssl_new_impl(struct event_base *base, + struct bufferevent *underlying, evutil_socket_t fd, void *ssl, + enum bufferevent_ssl_state state, int options, struct le_ssl_ops *ssl_ops); +struct bufferevent_ssl *bufferevent_ssl_upcast(struct bufferevent *bev); +void bufferevent_ssl_put_error( + struct bufferevent_ssl *bev_ssl, unsigned long err); +void bufferevent_ssl_stop_reading(struct bufferevent_ssl *bev_ssl); +void bufferevent_ssl_stop_writing(struct bufferevent_ssl *bev_ssl); +int bufferevent_ssl_renegotiate_impl(struct bufferevent *bev); +unsigned long bufferevent_get_ssl_error(struct bufferevent *bev); +int bufferevent_ssl_get_allow_dirty_shutdown(struct bufferevent *bev); +void bufferevent_ssl_set_allow_dirty_shutdown( + struct bufferevent *bev, int allow_dirty_shutdown); + +#endif /* SSL_COMPACT_H */ -- cgit v1.2.1