From b28effa95082e468340cc13d274d3a2ea205c174 Mon Sep 17 00:00:00 2001 From: Jesse Fang Date: Mon, 13 Jan 2020 23:04:29 +0800 Subject: [PATCH 1/9] mbedtls based SSL implementation prototype is libevent-2.1.11-stable libevent_openssl.c --- Makefile.am | 11 + bufferevent-internal.h | 7 + bufferevent_mbedtls.c | 1386 ++++++++++++++++++++++++++++++ include/event2/bufferevent_ssl.h | 81 +- libevent_mbedtls.pc.in | 16 + m4/libevent_mbedtls.m4 | 41 + 6 files changed, 1539 insertions(+), 3 deletions(-) create mode 100644 bufferevent_mbedtls.c create mode 100644 libevent_mbedtls.pc.in create mode 100644 m4/libevent_mbedtls.m4 diff --git a/Makefile.am b/Makefile.am index b66b42c2..61d4ff81 100644 --- a/Makefile.am +++ b/Makefile.am @@ -154,6 +154,10 @@ if OPENSSL LIBEVENT_LIBS_LA += libevent_openssl.la LIBEVENT_PKGCONFIG += libevent_openssl.pc endif +if MBEDTLS +LIBEVENT_LIBS_LA += libevent_mbedtls.la +LIBEVENT_PKGCONFIG += libevent_mbedtls.pc +endif if INSTALL_LIBEVENT lib_LTLIBRARIES = $(LIBEVENT_LIBS_LA) @@ -293,6 +297,13 @@ 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_LIBADD = $(MAYBE_CORE) $(MBEDTLS_LIBS) +libevent_mbedtls_la_LDFLAGS = $(GENERIC_LDFLAGS) +libevent_mbedtls_la_CPPFLAGS = $(AM_CPPFLAGS) $(MBEDTLS_INCS) +endif + noinst_HEADERS += \ WIN32-Code/getopt.h \ WIN32-Code/getopt.c \ diff --git a/bufferevent-internal.h b/bufferevent-internal.h index 87ab9ad9..94a9cb3c 100644 --- a/bufferevent-internal.h +++ b/bufferevent-internal.h @@ -313,6 +313,13 @@ extern const struct bufferevent_ops bufferevent_ops_openssl; #define BEV_IS_OPENSSL(bevp) 0 #endif +#if defined(EVENT__HAVE_MBEDTLS) +extern const struct bufferevent_ops bufferevent_ops_mbedtls; +#define BEV_IS_MBEDTLS(bevp) ((bevp)->be_ops == &bufferevent_ops_mbedtls) +#else +#define BEV_IS_MBEDTLS(bevp) 0 +#endif + #ifdef _WIN32 extern const struct bufferevent_ops bufferevent_ops_async; #define BEV_IS_ASYNC(bevp) ((bevp)->be_ops == &bufferevent_ops_async) diff --git a/bufferevent_mbedtls.c b/bufferevent_mbedtls.c new file mode 100644 index 00000000..22f0b7b1 --- /dev/null +++ b/bufferevent_mbedtls.c @@ -0,0 +1,1386 @@ +/* + * 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 +#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 + +/* + * Define an OpenSSL bio that targets a bufferevent. + */ + +/* -------------------- + A BIO is an OpenSSL abstraction that handles reading and writing data. The + library will happily speak SSL over anything that implements a BIO + interface. + + Here we define a BIO implementation that directs its output to a + bufferevent. We'll want to use this only when none of OpenSSL's built-in + IO mechanisms work for us. + -------------------- */ + +/* every BIO type needs its own integer type value. */ +#define BIO_TYPE_LIBEVENT 57 +/* ???? Arguably, we should set BIO_TYPE_FILTER or BIO_TYPE_SOURCE_SINK on + * this. */ + +#if 0 +static void +print_err(int val) +{ + int err; + printf("Error was %d\n", val); + + while ((err = ERR_get_error())) { + const char *msg = (const char*)ERR_reason_error_string(err); + const char *lib = (const char*)ERR_lib_error_string(err); + const char *func = (const char*)ERR_func_error_string(err); + + printf("%s in %s %s\n", msg, lib, func); + } +} +#else +#define print_err(v) ((void)0) +#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; + int r = 0; + struct evbuffer *input; + + //BIO_clear_retry_flags(b); + fprintf(stdout, "bio prepare write:\n"); + fwrite(out, 1, outlen, stdout); + + if (!out) + return 0; + if (!bufev) + return MBEDTLS_ERR_NET_INVALID_CONTEXT; + + input = bufferevent_get_input(bufev); + if (evbuffer_get_length(input) == 0) { + /* If there's no data to read, say so. */ + //BIO_set_retry_read(b); + return MBEDTLS_ERR_SSL_WANT_READ; + } else { + r = evbuffer_remove(input, out, outlen); + } + fprintf(stderr, "bio read %d bytes\n", r); + + return r; +} + +/* Called to write data info the BIO */ +static int +bio_bufferevent_write(void *ctx, const unsigned char *in, size_t inlen) +{ + struct bufferevent *bufev = (struct bufferevent*)ctx; + struct evbuffer *output; + size_t outlen; + + //BIO_clear_retry_flags(b); + + if (!bufev) + return MBEDTLS_ERR_NET_INVALID_CONTEXT; + + output = bufferevent_get_output(bufev); + outlen = evbuffer_get_length(output); + + /* 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 <= outlen) { + /* If no data can fit, we'll need to retry later. */ + //BIO_set_retry_write(b); + return MBEDTLS_ERR_SSL_WANT_WRITE; + } + inlen = bufev->wm_write.high - outlen; + } + + EVUTIL_ASSERT(inlen > 0); + evbuffer_add(output, in, inlen); + fprintf(stderr, "bio write %d bytes\n", inlen); + return inlen; +} + + +/* -------------------- + 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_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 = { + "ssl", + 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) +{ + int event = BEV_EVENT_ERROR; + //int dirty_shutdown = 0; + unsigned long err; + char buf[100] = {}; + + fprintf(stderr, "when %d error code %d", when, errcode); + + if (when & BEV_EVENT_READING && ret == 0) + { + if (bev_ssl->allow_dirty_shutdown) + event = BEV_EVENT_EOF; + } else { + mbedtls_strerror(errcode, buf, sizeof(buf)); + switch (errcode) { + 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); + break; + } + + put_error(bev_ssl, errcode); + } + + + stop_reading(bev_ssl); + stop_writing(bev_ssl); + + bufferevent_run_eventcb_(&bev_ssl->bev.bev, when | event, 0); +} + +/*static void +init_bio_counts(struct bufferevent_mbedtls *bev_ssl) +{ + BIO *rbio, *wbio; + + wbio = SSL_get_wbio(bev_ssl->ssl); + bev_ssl->counts.n_written = wbio ? BIO_number_written(wbio) : 0; + rbio = SSL_get_rbio(bev_ssl->ssl); + bev_ssl->counts.n_read = rbio ? BIO_number_read(rbio) : 0; +} + +static inline void +decrement_buckets(struct bufferevent_mbedtls *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)); + /* These next two subtractions can wrap around. That's okay. * / + unsigned long w = num_w - bev_ssl->counts.n_written; + unsigned long r = num_r - bev_ssl->counts.n_read; + if (w) + bufferevent_decrement_write_buckets_(&bev_ssl->bev, w); + if (r) + bufferevent_decrement_read_buckets_(&bev_ssl->bev, r); + bev_ssl->counts.n_written = num_w; + 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_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; + //decrement_buckets(bev_ssl); + } 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; + //decrement_buckets(bev_ssl); + } 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) +{ + 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; + } + //decrement_buckets(bev_ssl); + + 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; + } 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; + } + } +} + +static void +be_mbedtls_handshakecb(struct bufferevent *bev_base, void *ctx) +{ + struct bufferevent_mbedtls *bev_ssl = ctx; + do_handshake(bev_ssl);/* XXX handle failure */ +} + +static void +be_mbedtls_handshakeeventcb(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, 0); + } else + do_handshake(bev_ssl);/* XXX handle failure */ + bufferevent_decref_and_unlock_(&bev_ssl->bev.bev); +} + +static int +set_handshake_callbacks(struct bufferevent_mbedtls *bev_ssl, evutil_socket_t fd) +{ + 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; + } +} + +int +bufferevent_ssl_renegotiate(struct bufferevent *bev) +{ + struct bufferevent_mbedtls *bev_ssl = upcast(bev); + 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; +} + +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); + 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; + //SSL_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->conf->endpoint != MBEDTLS_SSL_IS_CLIENT) + 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) + 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) +{ + struct bufferevent_mbedtls *bev_ssl = upcast(bufev); + if (!bev_ssl) + return NULL; + return bev_ssl->ssl; +} + +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_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; + + if (bufferevent_init_common_(bev_p, base, + &bufferevent_ops_mbedtls, 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_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); + } + + bev_ssl->old_state = state; + bev_ssl->last_write = -1; + + //init_bio_counts(bev_ssl); + + 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_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) +{ + 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; +} + +struct bufferevent * +bufferevent_mbedtls_socket_new(struct event_base *base, + evutil_socket_t fd, + SSL *ssl, + enum bufferevent_ssl_state state, + int options) +{ + 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); + +err: + if (options & BEV_OPT_CLOSE_ON_FREE) + mbedtls_ssl_free(ssl); + return NULL; +} + +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); +} + +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]; + } + BEV_UNLOCK(bev); + return err; +} diff --git a/include/event2/bufferevent_ssl.h b/include/event2/bufferevent_ssl.h index 63f3a90b..00e64071 100644 --- a/include/event2/bufferevent_ssl.h +++ b/include/event2/bufferevent_ssl.h @@ -39,9 +39,6 @@ extern "C" { #endif -/* This is what openssl's SSL objects are underneath. */ -struct ssl_st; - /** The state of an SSL object to be used when creating a new SSL bufferevent. @@ -53,6 +50,9 @@ enum bufferevent_ssl_state { }; #if defined(EVENT__HAVE_OPENSSL) || defined(EVENT_IN_DOXYGEN_) +/* This is what openssl's SSL objects are underneath. */ +struct ssl_st; + /** Create a new SSL bufferevent to send its data over another bufferevent. @@ -125,6 +125,81 @@ int bufferevent_ssl_renegotiate(struct bufferevent *bev); EVENT2_EXPORT_SYMBOL unsigned long bufferevent_get_openssl_error(struct bufferevent *bev); +#endif +#if defined(EVENT__HAVE_MBEDTLS) || defined(EVENT_IN_DOXYGEN_) +struct mbedtls_ssl_context; +/** + Create a new SSL bufferevent to send its data over another bufferevent. + + @param base An event_base to use to detect reading and writing. It + must also be the base for the underlying bufferevent. + @param underlying A socket to use for this SSL + @param ssl A SSL* object from openssl. + @param state The current state of the SSL connection + @param options One or more bufferevent_options + @return A new bufferevent on success, or NULL on failure +*/ +EVENT2_EXPORT_SYMBOL +struct bufferevent * +bufferevent_mbedtls_filter_new(struct event_base *base, + struct bufferevent *underlying, + struct mbedtls_ssl_context *ssl, + enum bufferevent_ssl_state state, + int options); + +/** + Create a new SSL bufferevent to send its data over an SSL * on a socket. + + @param base An event_base to use to detect reading and writing + @param fd A socket to use for this SSL + @param ssl A SSL* object from mbedtls. + @param state The current state of the SSL connection + @param options One or more bufferevent_options + @return A new bufferevent on success, or NULL on failure. +*/ +EVENT2_EXPORT_SYMBOL +struct bufferevent * +bufferevent_mbedtls_socket_new(struct event_base *base, + evutil_socket_t fd, + struct mbedtls_ssl_context *ssl, + enum bufferevent_ssl_state state, + int options); + +/** Control how to report dirty SSL shutdowns. + + If the peer (or the network, or an attacker) closes the TCP + connection before closing the SSL channel, and the protocol is SSL >= v3, + this is a "dirty" shutdown. If allow_dirty_shutdown is 0 (default), + this is reported as BEV_EVENT_ERROR. + + If instead allow_dirty_shutdown=1, a dirty shutdown is reported as + BEV_EVENT_EOF. + + (Note that if the protocol is < SSLv3, you will always receive + BEV_EVENT_EOF, since SSL 2 and earlier cannot distinguish a secure + connection close from a dirty one. This is one reason (among many) + not to use SSL 2.) +*/ + +EVENT2_EXPORT_SYMBOL +int bufferevent_mbedtls_get_allow_dirty_shutdown(struct bufferevent *bev); +EVENT2_EXPORT_SYMBOL +void bufferevent_mbedtls_set_allow_dirty_shutdown(struct bufferevent *bev, + int allow_dirty_shutdown); + +/** Return the underlying mbedtls SSL * object for an SSL bufferevent. */ +EVENT2_EXPORT_SYMBOL +struct mbedtls_ssl_context * +bufferevent_mbedtls_get_ssl(struct bufferevent *bufev); + +/** Tells a bufferevent to begin SSL renegotiation. */ +EVENT2_EXPORT_SYMBOL +int bufferevent_ssl_renegotiate(struct bufferevent *bev); + +/** Return the most recent OpenSSL error reported on an SSL bufferevent. */ +EVENT2_EXPORT_SYMBOL +unsigned long bufferevent_get_mbedtls_error(struct bufferevent *bev); + #endif #ifdef __cplusplus diff --git a/libevent_mbedtls.pc.in b/libevent_mbedtls.pc.in new file mode 100644 index 00000000..822cb5da --- /dev/null +++ b/libevent_mbedtls.pc.in @@ -0,0 +1,16 @@ +#libevent pkg-config source file + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libevent_mbedtls +Description: libevent_mbedtls adds mbedtls-based TLS support to libevent +Version: @VERSION@ +Requires: libevent +Conflicts: +Libs: -L${libdir} -levent_mbedtls +Libs.private: @LIBS@ @MBEDTLS_LIBS@ +Cflags: -I${includedir} @MBEDTLS_INCS@ + diff --git a/m4/libevent_mbedtls.m4 b/m4/libevent_mbedtls.m4 new file mode 100644 index 00000000..946a9fdd --- /dev/null +++ b/m4/libevent_mbedtls.m4 @@ -0,0 +1,41 @@ +dnl ###################################################################### +dnl mbedtls support +AC_DEFUN([LIBEVENT_MBEDTLS], [ +AC_REQUIRE([NTP_PKG_CONFIG])dnl + +case "$enable_mbedtls" in + yes) + case "$have_mbedtls" in + yes) ;; + *) + save_LIBS="$LIBS" + LIBS="" + MBEDTLS_LIBS="" + for lib in mbedtls ; do + # clear cache + unset ac_cv_search_mbedtls_ssl_init + AC_SEARCH_LIBS([mbedtls_ssl_init], [mbedtls ], + [have_mbedtls=yes + MBEDTLS_LIBS="$LIBS -l$lib -lmbedcrypto -lmbedx509 $EV_LIB_GDI $EV_LIB_WS32 $MBEDTLS_LIBADD"], + [have_mbedtls=no], + [-l$lib $EV_LIB_GDI $EV_LIB_WS32 $MBEDTLS_LIBADD]) + LIBS="$save_LIBS" + test "$have_mbedtls" = "yes" && break + done + ;; + esac + CPPFLAGS_SAVE=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $MBEDTLS_INCS" + AC_CHECK_HEADERS([mbedtls/ssl.h], [], [have_mbedtls=no]) + CPPFLAGS=$CPPFLAGS_SAVE + AC_SUBST(MBEDTLS_INCS) + AC_SUBST(MBEDTLS_LIBS) + case "$have_mbedtls" in + yes) AC_DEFINE(HAVE_MBEDTLS, 1, [Define if the system has mbedtls]) ;; + esac + ;; +esac + +# check if we have and should use mbedtls +AM_CONDITIONAL(MBEDTLS, [test "$enable_mbedtls" != "no" && test "$have_mbedtls" = "yes"]) +]) From 7680409aa1b7512ef21d926c4c862c06582a6af3 Mon Sep 17 00:00:00 2001 From: Jesse Fang Date: Mon, 13 Jan 2020 23:08:26 +0800 Subject: [PATCH 2/9] simple https client example using mbedtls Based on mbedtls's source code programs/ssl/ssl_client1.c --- sample/ssl_client1.c | 315 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 315 insertions(+) create mode 100644 sample/ssl_client1.c diff --git a/sample/ssl_client1.c b/sample/ssl_client1.c new file mode 100644 index 00000000..af8019c9 --- /dev/null +++ b/sample/ssl_client1.c @@ -0,0 +1,315 @@ +/* + * SSL client demonstration program + * + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +#if !defined(MBEDTLS_CONFIG_FILE) +#include "mbedtls/config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +#if defined(MBEDTLS_PLATFORM_C) +#include "mbedtls/platform.h" +#else +#include +#include +#define mbedtls_time time +#define mbedtls_time_t time_t +#define mbedtls_fprintf fprintf +#define mbedtls_printf printf +#endif + +#if !defined(MBEDTLS_BIGNUM_C) || !defined(MBEDTLS_ENTROPY_C) || \ + !defined(MBEDTLS_SSL_TLS_C) || !defined(MBEDTLS_SSL_CLI_C) || \ + !defined(MBEDTLS_NET_C) || !defined(MBEDTLS_RSA_C) || \ + !defined(MBEDTLS_CERTS_C) || !defined(MBEDTLS_PEM_PARSE_C) || \ + !defined(MBEDTLS_CTR_DRBG_C) || !defined(MBEDTLS_X509_CRT_PARSE_C) +int main( void ) +{ + mbedtls_printf("MBEDTLS_BIGNUM_C and/or MBEDTLS_ENTROPY_C and/or " + "MBEDTLS_SSL_TLS_C and/or MBEDTLS_SSL_CLI_C and/or " + "MBEDTLS_NET_C and/or MBEDTLS_RSA_C and/or " + "MBEDTLS_CTR_DRBG_C and/or MBEDTLS_X509_CRT_PARSE_C " + "not defined.\n"); + return( 0 ); +} +#else + +#include "mbedtls/net_sockets.h" +#include "mbedtls/debug.h" +#include "mbedtls/ssl.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/error.h" +#include "mbedtls/certs.h" + +#include + +#include +#include +#include +#include + +#define SERVER_PORT "443" +#define SERVER_NAME "amazon.com" +#define GET_REQUEST "GET / HTTP/1.0\r\n\r\n" + +#define DEBUG_LEVEL 1 + +static void my_debug( void *ctx, int level, + const char *file, int line, + const char *str ) +{ + ((void) level); + + mbedtls_fprintf( (FILE *) ctx, "%s:%04d: %s", file, line, str ); + fflush( (FILE *) ctx ); +} + +void writecb(struct bufferevent *bev, void *arg) +{ + fprintf(stderr, "writecb\n"); +} + +void readcb(struct bufferevent *bev, void *arg) +{ + char buf[1000] = {}; + size_t r = 0; + int i; + for (i=0; i<10; ++i) +{ + r = bufferevent_read(bev, buf, 800); + fprintf(stderr, "readcb %d\n\n", r); + if (r > 1) { + fwrite(buf, 1, r, stdout); + fwrite("\n", 1, r, stdout); + fflush(stdout); + } + } +} + +void eventcb(struct bufferevent *bev, short what, void *arg) +{ + fprintf(stderr, "\n---------------eventcb %d\n", what); + if (what & BEV_EVENT_CONNECTED) { + const char headers[] = + "GET / HTTP/1.1\r\n" + "HOST: " SERVER_NAME "\r\n" + "User-Agent: curl/7.65.1\r\n" + "Connection: Keep-Alive\r\n" + "\r\n"; + bufferevent_write(bev, headers, sizeof(headers) - 1); // without ending '\0' + //bufferevent_disable(bev, EV_WRITE); + fprintf(stderr, "write request completely\n"); + } else if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) { + fprintf(stderr, "closed\n"); + bufferevent_free(bev); + } +} + + + +int main( void ) +{ + int ret, len; + mbedtls_net_context server_fd; + uint32_t flags; + unsigned char buf[1024]; + const char *pers = "ssl_client1"; + + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ssl_context ssl; + mbedtls_ssl_config conf; + mbedtls_x509_crt cacert; + + struct event *ev_sigterm; + struct event_base *evbase; + struct evdns_base *evdns; + +#ifdef WIN32 + WORD wVersionRequested; + WSADATA wsaData; + int err; + + /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */ + wVersionRequested = MAKEWORD(2, 2); + + err = WSAStartup(wVersionRequested, &wsaData); +#endif + + +#if defined(MBEDTLS_DEBUG_C) + mbedtls_debug_set_threshold( DEBUG_LEVEL ); +#endif + + /* + * 0. Initialize the RNG and the session data + */ + mbedtls_net_init( &server_fd ); + mbedtls_ssl_init( &ssl ); + mbedtls_ssl_config_init( &conf ); + mbedtls_x509_crt_init( &cacert ); + mbedtls_ctr_drbg_init( &ctr_drbg ); + + mbedtls_printf( "\n . Seeding the random number generator..." ); + fflush( stdout ); + + mbedtls_entropy_init( &entropy ); + if( ( ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy, + (const unsigned char *) pers, + strlen( pers ) ) ) != 0 ) + { + mbedtls_printf( " failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret ); + goto exit; + } + + mbedtls_printf( " ok\n" ); + + /* + * 0. Initialize certificates + */ + mbedtls_printf( " . Loading the CA root certificate ..." ); + fflush( stdout ); + + ret = mbedtls_x509_crt_parse( &cacert, (const unsigned char *) mbedtls_test_cas_pem, + mbedtls_test_cas_pem_len ); + if( ret < 0 ) + { + mbedtls_printf( " failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n", -ret ); + goto exit; + } + + mbedtls_printf( " ok (%d skipped)\n", ret ); + + /* + * 1. Start the connection + */ + mbedtls_printf( " . Connecting to tcp/%s/%s...", SERVER_NAME, SERVER_PORT ); + fflush( stdout ); + + if( ( ret = mbedtls_net_connect( &server_fd, SERVER_NAME, + SERVER_PORT, MBEDTLS_NET_PROTO_TCP ) ) != 0 ) + { + mbedtls_printf( " failed\n ! mbedtls_net_connect returned %d\n\n", ret ); + goto exit; + } + + mbedtls_printf( " ok\n" ); + + /* + * 2. Setup stuff + */ + mbedtls_printf( " . Setting up the SSL/TLS structure..." ); + fflush( stdout ); + + if( ( ret = mbedtls_ssl_config_defaults( &conf, + MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT ) ) != 0 ) + { + mbedtls_printf( " failed\n ! mbedtls_ssl_config_defaults returned %d\n\n", ret ); + goto exit; + } + + mbedtls_printf( " ok\n" ); + + /* OPTIONAL is not optimal for security, + * but makes interop easier in this simplified example */ + mbedtls_ssl_conf_authmode( &conf, MBEDTLS_SSL_VERIFY_NONE ); + mbedtls_ssl_conf_ca_chain( &conf, &cacert, NULL ); + mbedtls_ssl_conf_rng( &conf, mbedtls_ctr_drbg_random, &ctr_drbg ); + mbedtls_ssl_conf_dbg( &conf, my_debug, stdout ); + + if( ( ret = mbedtls_ssl_setup( &ssl, &conf ) ) != 0 ) + { + mbedtls_printf( " failed\n ! mbedtls_ssl_setup returned %d\n\n", ret ); + goto exit; + } + + if( ( ret = mbedtls_ssl_set_hostname( &ssl, SERVER_NAME ) ) != 0 ) + { + mbedtls_printf( " failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", ret ); + goto exit; + } + fflush( stdout ); + + //mbedtls_ssl_set_bio( &ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, NULL ); + + event_enable_debug_mode(); + evbase = event_base_new(); + evdns = evdns_base_new(evbase, 1); + evdns_base_set_option(evdns, "randomize-case:", "0"); + + evutil_make_socket_nonblocking(server_fd.fd); + + +#if 1 +struct bufferevent *bev = bufferevent_socket_new(evbase, server_fd.fd, BEV_OPT_CLOSE_ON_FREE); +struct bufferevent *bevf = bufferevent_mbedtls_filter_new( + evbase, bev, &ssl, + BUFFEREVENT_SSL_CONNECTING, BEV_OPT_CLOSE_ON_FREE); + bev = bevf; +#else + +struct bufferevent *bev = bufferevent_mbedtls_socket_new( + evbase, server_fd.fd, &ssl, + BUFFEREVENT_SSL_CONNECTING, BEV_OPT_CLOSE_ON_FREE); +#endif + bufferevent_setcb(bev, readcb, NULL, eventcb, NULL); + + bufferevent_enable(bev, EV_READ); + + + event_base_loop(evbase, 0); + event_base_free(evbase); + + + +exit: + +#ifdef MBEDTLS_ERROR_C + if( ret != 0 ) + { + char error_buf[100]; + mbedtls_strerror( ret, error_buf, 100 ); + mbedtls_printf("Last error was: %d - %s\n\n", ret, error_buf ); + } +#endif + + mbedtls_net_free( &server_fd ); + + mbedtls_x509_crt_free( &cacert ); + mbedtls_ssl_free( &ssl ); + mbedtls_ssl_config_free( &conf ); + mbedtls_ctr_drbg_free( &ctr_drbg ); + mbedtls_entropy_free( &entropy ); + +#if defined(_WIN32) + mbedtls_printf( " + Press Enter to exit this program.\n" ); + fflush( stdout ); getchar(); +#endif + + return( ret ); +} +#endif /* MBEDTLS_BIGNUM_C && MBEDTLS_ENTROPY_C && MBEDTLS_SSL_TLS_C && + MBEDTLS_SSL_CLI_C && MBEDTLS_NET_C && MBEDTLS_RSA_C && + MBEDTLS_CERTS_C && MBEDTLS_PEM_PARSE_C && MBEDTLS_CTR_DRBG_C && + MBEDTLS_X509_CRT_PARSE_C */ From 8218777d44ab30c8a5e2d5d637a62f924c83b65c Mon Sep 17 00:00:00 2001 From: Jesse Fang Date: Tue, 14 Jan 2020 12:18:27 +0800 Subject: [PATCH 3/9] mbed TLS cmake support FindMbedTLS.cmake is come from https://github.com/AVSystem/avs_commons/blob/master/cmake/FindMbedTLS.cmake, which is licensed under Apache 2.0 alternatives: https://github.com/curl/curl/blob/master/CMake/FindMbedTLS.cmake without variable MBEDTLS_ROOT_DIR https://github.com/libgit2/libgit2/blob/master/cmake/Modules/FindmbedTLS.cmake GPLv2 with a special Linking Exception --- CMakeLists.txt | 24 ++++++ cmake/FindMbedTLS.cmake | 159 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 cmake/FindMbedTLS.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 960e2e1e..f3072aa4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -132,6 +132,9 @@ option(EVENT__DISABLE_THREAD_SUPPORT option(EVENT__DISABLE_OPENSSL "Define if libevent should build without support for OpenSSL encryption" OFF) +option(EVENT__DISABLE_MBEDTLS + "Define if libevent should build without support for mbed TLS encryption" OFF) + option(EVENT__DISABLE_BENCHMARK "Defines if libevent should build without the benchmark executables" OFF) @@ -866,6 +869,21 @@ if (NOT EVENT__DISABLE_OPENSSL) list(APPEND LIB_APPS ${OPENSSL_LIBRARIES}) endif() +if (NOT EVENT__DISABLE_MBEDTLS) + find_package(MbedTLS REQUIRED) + + set(EVENT__HAVE_MBEDTLS 1) + + message(STATUS "mbed TLS include: ${MBEDTLS_INCLUDE_DIR}") + message(STATUS "mbed TLS lib: ${MBEDTLS_LIBRARIES}") + + include_directories(${MBEDTLS_INCLUDE_DIR}) + + list(APPEND SRC_MBEDTLS bufferevent_mbedtls.c) + list(APPEND HDR_PUBLIC include/event2/bufferevent_ssl.h) + list(APPEND LIB_APPS ${MBEDTLS_LIBRARIES}) +endif() + if (NOT EVENT__DISABLE_THREAD_SUPPORT) if (WIN32) list(APPEND SRC_CORE evthread_win32.c) @@ -971,6 +989,12 @@ if (NOT EVENT__DISABLE_OPENSSL) SOURCES ${SRC_OPENSSL}) endif() +if (NOT EVENT__DISABLE_MBEDTLS) + add_event_library(event_mbedtls + LIBRARIES event_core_shared ${MBEDTLS_LIBRARIES} + SOURCES ${SRC_MBEDTLS}) +endif() + if (EVENT__HAVE_PTHREADS) set(SRC_PTHREADS evthread_pthread.c) add_event_library(event_pthreads diff --git a/cmake/FindMbedTLS.cmake b/cmake/FindMbedTLS.cmake new file mode 100644 index 00000000..d82baa41 --- /dev/null +++ b/cmake/FindMbedTLS.cmake @@ -0,0 +1,159 @@ +# Copyright 2017-2019 AVSystem +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#.rst: +# FindMbedTLS +# ----------- +# +# Find the mbedTLS encryption library. +# +# Imported Targets +# ^^^^^^^^^^^^^^^^ +# +# This module defines the following :prop_tgt:`IMPORTED` targets: +# +# ``mbedtls`` +# The mbedTLS ``mbedtls`` library, if found. +# ``mbedcrypto`` +# The mbedtls ``crypto`` library, if found. +# ``mbedx509`` +# The mbedtls ``x509`` library, if found. +# +# Result Variables +# ^^^^^^^^^^^^^^^^ +# +# This module will set the following variables in your project: +# +# ``MBEDTLS_FOUND`` +# System has the mbedTLS library. +# ``MBEDTLS_INCLUDE_DIR`` +# The mbedTLS include directory. +# ``MBEDTLS_LIBRARY`` +# The mbedTLS SSL library. +# ``MBEDTLS_CRYPTO_LIBRARY`` +# The mbedTLS crypto library. +# ``MBEDTLS_X509_LIBRARY`` +# The mbedTLS x509 library. +# ``MBEDTLS_LIBRARIES`` +# All mbedTLS libraries. +# ``MBEDTLS_VERSION`` +# This is set to ``$major.$minor.$patch``. +# ``MBEDTLS_VERSION_MAJOR`` +# Set to major mbedTLS version number. +# ``MBEDTLS_VERSION_MINOR`` +# Set to minor mbedTLS version number. +# ``MBEDTLS_VERSION_PATCH`` +# Set to patch mbedTLS version number. +# +# Hints +# ^^^^^ +# +# Set ``MBEDTLS_ROOT_DIR`` to the root directory of an mbedTLS installation. +# Set ``MBEDTLS_USE_STATIC_LIBS`` to ``TRUE`` to look for static libraries. + +if(MBEDTLS_ROOT_DIR) + # Disable re-rooting paths in find_path/find_library. + # This assumes MBEDTLS_ROOT_DIR is an absolute path. + set(_EXTRA_FIND_ARGS "NO_CMAKE_FIND_ROOT_PATH") +endif() + +find_path(MBEDTLS_INCLUDE_DIR + NAMES mbedtls/ssl.h + PATH_SUFFIXES include + HINTS ${MBEDTLS_ROOT_DIR} + ${_EXTRA_FIND_ARGS}) + +# based on https://github.com/ARMmbed/mbedtls/issues/298 +if(MBEDTLS_INCLUDE_DIR AND EXISTS "${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h") + file(STRINGS "${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h" VERSION_STRING_LINE REGEX "^#define MBEDTLS_VERSION_STRING[ \\t\\n\\r]+\"[^\"]*\"$") + file(STRINGS "${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h" VERSION_MAJOR_LINE REGEX "^#define MBEDTLS_VERSION_MAJOR[ \\t\\n\\r]+[0-9]+$") + file(STRINGS "${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h" VERSION_MINOR_LINE REGEX "^#define MBEDTLS_VERSION_MINOR[ \\t\\n\\r]+[0-9]+$") + file(STRINGS "${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h" VERSION_PATCH_LINE REGEX "^#define MBEDTLS_VERSION_PATCH[ \\t\\n\\r]+[0-9]+$") + + string(REGEX REPLACE "^#define MBEDTLS_VERSION_STRING[ \\t\\n\\r]+\"([^\"]*)\"$" "\\1" MBEDTLS_VERSION "${VERSION_STRING_LINE}") + string(REGEX REPLACE "^#define MBEDTLS_VERSION_MAJOR[ \\t\\n\\r]+([0-9]+)$" "\\1" MBEDTLS_VERSION_MAJOR "${VERSION_MAJOR_LINE}") + string(REGEX REPLACE "^#define MBEDTLS_VERSION_MINOR[ \\t\\n\\r]+([0-9]+)$" "\\1" MBEDTLS_VERSION_MINOR "${VERSION_MINOR_LINE}") + string(REGEX REPLACE "^#define MBEDTLS_VERSION_PATCH[ \\t\\n\\r]+([0-9]+)$" "\\1" MBEDTLS_VERSION_PATCH "${VERSION_PATCH_LINE}") +endif() + + +if(MBEDTLS_USE_STATIC_LIBS) + set(_MBEDTLS_LIB_NAME libmbedtls.a) + set(_MBEDTLS_CRYPTO_LIB_NAME libmbedcrypto.a) + set(_MBEDTLS_X509_LIB_NAME libmbedx509.a) +else() + set(_MBEDTLS_LIB_NAME mbedtls) + set(_MBEDTLS_CRYPTO_LIB_NAME mbedcrypto) + set(_MBEDTLS_X509_LIB_NAME mbedx509) +endif() + +find_library(MBEDTLS_LIBRARY + NAMES ${_MBEDTLS_LIB_NAME} + PATH_SUFFIXES lib + HINTS ${MBEDTLS_ROOT_DIR} + ${_EXTRA_FIND_ARGS}) + +find_library(MBEDTLS_CRYPTO_LIBRARY + NAMES ${_MBEDTLS_CRYPTO_LIB_NAME} + PATH_SUFFIXES lib + HINTS ${MBEDTLS_ROOT_DIR} + ${_EXTRA_FIND_ARGS}) + +find_library(MBEDTLS_X509_LIBRARY + NAMES ${_MBEDTLS_X509_LIB_NAME} + PATH_SUFFIXES lib + HINTS ${MBEDTLS_ROOT_DIR} + ${_EXTRA_FIND_ARGS}) + +set(MBEDTLS_LIBRARIES ${MBEDTLS_LIBRARY} ${MBEDTLS_CRYPTO_LIBRARY} ${MBEDTLS_X509_LIBRARY}) + +if(MBEDTLS_INCLUDE_DIR) + set(MBEDTLS_FOUND TRUE) +endif() + + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(mbedTLS + FOUND_VAR MBEDTLS_FOUND + REQUIRED_VARS + MBEDTLS_INCLUDE_DIR + MBEDTLS_LIBRARY + MBEDTLS_CRYPTO_LIBRARY + MBEDTLS_X509_LIBRARY + MBEDTLS_LIBRARIES + MBEDTLS_VERSION + VERSION_VAR MBEDTLS_VERSION) + + +if(NOT TARGET mbedtls) + add_library(mbedtls UNKNOWN IMPORTED) + set_target_properties(mbedtls PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${MBEDTLS_INCLUDE_DIR}" + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION "${MBEDTLS_LIBRARY}") +endif() + +if(NOT TARGET mbedcrypto) + add_library(mbedcrypto UNKNOWN IMPORTED) + set_target_properties(mbedcrypto PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION "${MBEDTLS_CRYPTO_LIBRARY}") +endif() + +if(NOT TARGET mbedx509) + add_library(mbedx509 UNKNOWN IMPORTED) + set_target_properties(mbedx509 PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION "${MBEDTLS_X509_LIBRARY}") +endif() From 028385f685585b4b247bdd4acae3cd12de2b4da4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?okhowang=28=E7=8E=8B=E6=B2=9B=E6=96=87=29?= Date: Thu, 28 May 2020 17:14:46 +0800 Subject: [PATCH 4/9] fix build system and add test and cleanup code --- CMakeLists.txt | 28 ++- Makefile.am | 4 +- bufferevent_mbedtls.c | 115 ++--------- cmake/FindMbedTLS.cmake | 2 +- configure.ac | 11 +- event-config.h.cmake | 3 + include/event2/bufferevent_ssl.h | 2 +- include/event2/visibility.h | 3 +- m4/libevent_mbedtls.m4 | 21 +- sample/include.am | 7 + sample/ssl-client-mbedtls.c | 264 ++++++++++++++++++++++++ sample/ssl_client1.c | 315 ----------------------------- test-export/test-export.c | 51 +++++ test-export/test-export.py | 7 + test/include.am | 8 +- test/regress.h | 1 + test/regress_main.c | 3 + test/regress_mbedtls.c | 335 +++++++++++++++++++++++++++++++ test/regress_openssl.c | 311 ++++++++++++++++++++++++++++ test/regress_ssl.c | 326 +++--------------------------- 20 files changed, 1081 insertions(+), 736 deletions(-) create mode 100644 sample/ssl-client-mbedtls.c delete mode 100644 sample/ssl_client1.c create mode 100644 test/regress_mbedtls.c create mode 100644 test/regress_openssl.c diff --git a/CMakeLists.txt b/CMakeLists.txt index f3072aa4..90052916 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -991,7 +991,9 @@ endif() if (NOT EVENT__DISABLE_MBEDTLS) add_event_library(event_mbedtls - LIBRARIES event_core_shared ${MBEDTLS_LIBRARIES} + INNER_LIBRARIES event_core + OUTER_INCLUDES ${MBEDTLS_INCLUDE_DIR} + LIBRARIES ${MBEDTLS_LIBRARIES} SOURCES ${SRC_MBEDTLS}) endif() @@ -1038,8 +1040,8 @@ macro(add_sample_prog ssl name) ${LIB_APPS} ${LIB_PLATFORM}) - if (${ssl}) - target_link_libraries(${name} event_openssl) + if (TARGET ${ssl}) + target_link_libraries(${name} ${ssl}) if(WIN32) target_link_libraries(${name} crypt32) endif() @@ -1062,13 +1064,18 @@ if (NOT EVENT__DISABLE_SAMPLES) endif() if (NOT EVENT__DISABLE_OPENSSL) - add_sample_prog(ON https-client + add_sample_prog(event_openssl https-client sample/https-client.c sample/openssl_hostname_validation.c sample/hostcheck.c) - add_sample_prog(ON le-proxy + add_sample_prog(event_openssl le-proxy sample/le-proxy.c) - add_sample_prog(ON becat sample/becat.c ${WIN32_GETOPT}) + add_sample_prog(event_openssl becat sample/becat.c ${WIN32_GETOPT}) + endif() + + if (NOT EVENT__DISABLE_MBEDTLS) + add_sample_prog(event_mbedtls ssl-client-mbedtls + sample/ssl-client-mbedtls.c) endif() set(SAMPLES_WOPT @@ -1180,7 +1187,11 @@ if (NOT EVENT__DISABLE_TESTS) endif() if (NOT EVENT__DISABLE_OPENSSL) - list(APPEND SRC_REGRESS test/regress_ssl.c) + list(APPEND SRC_REGRESS test/regress_openssl.c) + endif() + + if (NOT EVENT__DISABLE_MBEDTLS) + list(APPEND SRC_REGRESS test/regress_mbedtls.c) endif() add_executable(regress ${SRC_REGRESS}) @@ -1193,6 +1204,9 @@ if (NOT EVENT__DISABLE_TESTS) if (NOT EVENT__DISABLE_OPENSSL) target_link_libraries(regress event_openssl) endif() + if (NOT EVENT__DISABLE_MBEDTLS) + target_link_libraries(regress event_mbedtls) + endif() if (CMAKE_USE_PTHREADS_INIT) target_link_libraries(regress event_pthreads) endif() diff --git a/Makefile.am b/Makefile.am index 61d4ff81..a004e898 100644 --- a/Makefile.am +++ b/Makefile.am @@ -101,7 +101,8 @@ LIBEVENT_PKGCONFIG=libevent.pc libevent_core.pc libevent_extra.pc # included from other files. PLATFORM_DEPENDENT_SRC = \ arc4random.c \ - epoll_sub.c + epoll_sub.c \ + test/regress_ssl.c CMAKE_FILES = \ cmake/AddCompilerFlags.cmake \ @@ -116,6 +117,7 @@ CMAKE_FILES = \ cmake/CodeCoverage.cmake \ cmake/COPYING-CMAKE-SCRIPTS \ cmake/Copyright.txt \ + cmake/FindMbedTLS.cmake \ cmake/LibeventConfig.cmake.in \ cmake/LibeventConfigVersion.cmake.in \ cmake/Macros.cmake \ diff --git a/bufferevent_mbedtls.c b/bufferevent_mbedtls.c index 22f0b7b1..5dc95f47 100644 --- a/bufferevent_mbedtls.c +++ b/bufferevent_mbedtls.c @@ -70,39 +70,13 @@ #define SSL_ERROR_WANT_WRITE MBEDTLS_ERR_SSL_WANT_WRITE #define SSL mbedtls_ssl_context -/* - * Define an OpenSSL bio that targets a bufferevent. - */ - -/* -------------------- - A BIO is an OpenSSL abstraction that handles reading and writing data. The - library will happily speak SSL over anything that implements a BIO - interface. - - Here we define a BIO implementation that directs its output to a - bufferevent. We'll want to use this only when none of OpenSSL's built-in - IO mechanisms work for us. - -------------------- */ - -/* every BIO type needs its own integer type value. */ -#define BIO_TYPE_LIBEVENT 57 -/* ???? Arguably, we should set BIO_TYPE_FILTER or BIO_TYPE_SOURCE_SINK on - * this. */ - #if 0 static void print_err(int val) { - int err; - printf("Error was %d\n", val); - - while ((err = ERR_get_error())) { - const char *msg = (const char*)ERR_reason_error_string(err); - const char *lib = (const char*)ERR_lib_error_string(err); - const char *func = (const char*)ERR_func_error_string(err); - - printf("%s in %s %s\n", msg, lib, func); - } + char buf[1024]; + mbedtls_strerror(val, buf, sizeof(buf)); + printf("Error was %d:%s\n", val, buf); } #else #define print_err(v) ((void)0) @@ -117,10 +91,6 @@ bio_bufferevent_read(void *ctx, unsigned char *out, size_t outlen) int r = 0; struct evbuffer *input; - //BIO_clear_retry_flags(b); - fprintf(stdout, "bio prepare write:\n"); - fwrite(out, 1, outlen, stdout); - if (!out) return 0; if (!bufev) @@ -129,17 +99,15 @@ bio_bufferevent_read(void *ctx, unsigned char *out, size_t outlen) input = bufferevent_get_input(bufev); if (evbuffer_get_length(input) == 0) { /* If there's no data to read, say so. */ - //BIO_set_retry_read(b); return MBEDTLS_ERR_SSL_WANT_READ; } else { r = evbuffer_remove(input, out, outlen); } - fprintf(stderr, "bio read %d bytes\n", r); return r; } -/* Called to write data info the BIO */ +/* Called to write data into the BIO */ static int bio_bufferevent_write(void *ctx, const unsigned char *in, size_t inlen) { @@ -147,8 +115,6 @@ bio_bufferevent_write(void *ctx, const unsigned char *in, size_t inlen) struct evbuffer *output; size_t outlen; - //BIO_clear_retry_flags(b); - if (!bufev) return MBEDTLS_ERR_NET_INVALID_CONTEXT; @@ -160,7 +126,6 @@ bio_bufferevent_write(void *ctx, const unsigned char *in, size_t 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); return MBEDTLS_ERR_SSL_WANT_WRITE; } inlen = bufev->wm_write.high - outlen; @@ -168,22 +133,15 @@ bio_bufferevent_write(void *ctx, const unsigned char *in, size_t inlen) EVUTIL_ASSERT(inlen > 0); evbuffer_add(output, in, inlen); - fprintf(stderr, "bio write %d bytes\n", inlen); return inlen; } /* -------------------- - Now, here's the OpenSSL-based implementation of bufferevent. + Now, here's the mbedTLS-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.) + The implementation comes in only one flavors, that has the + SSL object connect to a socket directly. -------------------- */ struct bio_data_counts { @@ -201,8 +159,8 @@ struct bufferevent_mbedtls { /* 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; + /* net fd */ + mbedtls_net_context net_ctx; /* The SSL object doing our encryption. */ SSL *ssl; @@ -248,7 +206,7 @@ static int be_mbedtls_flush(struct bufferevent *bufev, static int be_mbedtls_ctrl(struct bufferevent *, enum bufferevent_ctrl_op, union bufferevent_ctrl_data *); const struct bufferevent_ops bufferevent_ops_mbedtls = { - "ssl", + "mbedtls", evutil_offsetof(struct bufferevent_mbedtls, bev.bev), be_mbedtls_enable, be_mbedtls_disable, @@ -407,10 +365,7 @@ conn_closed(struct bufferevent_mbedtls *bev_ssl, int when, int errcode, int ret) { int event = BEV_EVENT_ERROR; //int dirty_shutdown = 0; - unsigned long err; - char buf[100] = {}; - - fprintf(stderr, "when %d error code %d", when, errcode); + char buf[100]; if (when & BEV_EVENT_READING && ret == 0) { @@ -443,33 +398,6 @@ conn_closed(struct bufferevent_mbedtls *bev_ssl, int when, int errcode, int ret) bufferevent_run_eventcb_(&bev_ssl->bev.bev, when | event, 0); } -/*static void -init_bio_counts(struct bufferevent_mbedtls *bev_ssl) -{ - BIO *rbio, *wbio; - - wbio = SSL_get_wbio(bev_ssl->ssl); - bev_ssl->counts.n_written = wbio ? BIO_number_written(wbio) : 0; - rbio = SSL_get_rbio(bev_ssl->ssl); - bev_ssl->counts.n_read = rbio ? BIO_number_read(rbio) : 0; -} - -static inline void -decrement_buckets(struct bufferevent_mbedtls *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)); - /* These next two subtractions can wrap around. That's okay. * / - unsigned long w = num_w - bev_ssl->counts.n_written; - unsigned long r = num_r - bev_ssl->counts.n_read; - if (w) - bufferevent_decrement_write_buckets_(&bev_ssl->bev, w); - if (r) - bufferevent_decrement_read_buckets_(&bev_ssl->bev, r); - bev_ssl->counts.n_written = num_w; - bev_ssl->counts.n_read = num_r; -}*/ - #define OP_MADE_PROGRESS 1 #define OP_BLOCKED 2 #define OP_ERR 4 @@ -507,7 +435,6 @@ do_read(struct bufferevent_mbedtls *bev_ssl, int n_to_read) { return OP_ERR | result; ++n_used; space[i].iov_len = r; - //decrement_buckets(bev_ssl); } else { int err = r; print_err(err); @@ -584,7 +511,6 @@ do_write(struct bufferevent_mbedtls *bev_ssl, int atmost) return OP_ERR | result; n_written += r; bev_ssl->last_write = -1; - //decrement_buckets(bev_ssl); } else { int err = r; print_err(err); @@ -924,7 +850,6 @@ do_handshake(struct bufferevent_mbedtls *bev_ssl) r = mbedtls_ssl_handshake(bev_ssl->ssl); break; } - //decrement_buckets(bev_ssl); if (r==0) { evutil_socket_t fd = event_get_fd(&bev_ssl->bev.bev.ev_read); @@ -1010,7 +935,7 @@ set_handshake_callbacks(struct bufferevent_mbedtls *bev_ssl, evutil_socket_t fd) } int -bufferevent_ssl_renegotiate(struct bufferevent *bev) +bufferevent_mbedtls_renegotiate(struct bufferevent *bev) { struct bufferevent_mbedtls *bev_ssl = upcast(bev); if (!bev_ssl) @@ -1103,7 +1028,9 @@ be_mbedtls_unlink(struct bufferevent *bev) } else { mbedtls_ssl_set_bio(bev_ssl->ssl, NULL, NULL, NULL, NULL); bufferevent_free(bev_ssl->underlying); - bev_ssl->underlying = NULL; + /* We still have a reference to it, via our + * BIO. So we don't drop this. */ + // bev_ssl->underlying = NULL; } } } else { @@ -1169,14 +1096,12 @@ be_mbedtls_set_fd(struct bufferevent_mbedtls *bev_ssl, case BUFFEREVENT_SSL_ACCEPTING: if (bev_ssl->ssl->conf->endpoint != MBEDTLS_SSL_IS_SERVER) 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 (bev_ssl->ssl->conf->endpoint != MBEDTLS_SSL_IS_CLIENT) return -1; - //SSL_set_connect_state(bev_ssl->ssl); if (set_handshake_callbacks(bev_ssl, fd) < 0) return -1; break; @@ -1256,10 +1181,6 @@ bufferevent_mbedtls_new_impl(struct event_base *base, &bufferevent_ops_mbedtls, 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; @@ -1277,8 +1198,6 @@ bufferevent_mbedtls_new_impl(struct event_base *base, bev_ssl->old_state = state; bev_ssl->last_write = -1; - //init_bio_counts(bev_ssl); - fd = be_mbedtls_auto_fd(bev_ssl, fd); if (be_mbedtls_set_fd(bev_ssl, state, fd)) goto err; @@ -1340,10 +1259,6 @@ bufferevent_mbedtls_socket_new(struct event_base *base, return bufferevent_mbedtls_new_impl( base, NULL, fd, ssl, state, options); -err: - if (options & BEV_OPT_CLOSE_ON_FREE) - mbedtls_ssl_free(ssl); - return NULL; } int diff --git a/cmake/FindMbedTLS.cmake b/cmake/FindMbedTLS.cmake index d82baa41..c430269d 100644 --- a/cmake/FindMbedTLS.cmake +++ b/cmake/FindMbedTLS.cmake @@ -124,7 +124,7 @@ endif() include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(mbedTLS +find_package_handle_standard_args(MbedTLS FOUND_VAR MBEDTLS_FOUND REQUIRED_VARS MBEDTLS_INCLUDE_DIR diff --git a/configure.ac b/configure.ac index 7caa85a4..2599c225 100644 --- a/configure.ac +++ b/configure.ac @@ -60,6 +60,9 @@ AC_ARG_ENABLE(malloc-replacement, AC_ARG_ENABLE(openssl, AS_HELP_STRING(--disable-openssl, disable support for openssl encryption), [], [enable_openssl=yes]) +AC_ARG_ENABLE(mbedtls, + AS_HELP_STRING(--disable-mbedtls, disable support for mbedtls encryption), + [], [enable_mbedtls=yes]) AC_ARG_ENABLE(debug-mode, AS_HELP_STRING(--disable-debug-mode, disable support for running in debug mode), [], [enable_debug_mode=yes]) @@ -179,6 +182,7 @@ AC_SUBST(OPENSSL_LIBADD) AC_SYS_LARGEFILE LIBEVENT_OPENSSL +LIBEVENT_MBEDTLS dnl Checks for header files. AC_CHECK_HEADERS([ \ @@ -804,7 +808,7 @@ if test x$enable_debug_mode = xno; then [Define if libevent should build without support for a debug mode]) fi -dnl check if we should enable verbose debugging +dnl check if we should enable verbose debugging if test x$enable_verbose_debug = xyes; then CFLAGS="$CFLAGS -DUSE_DEBUG" fi @@ -812,6 +816,9 @@ fi dnl check if we have and should use OpenSSL AM_CONDITIONAL(OPENSSL, [test "$enable_openssl" != "no" && test "$have_openssl" = "yes"]) +# check if we have and should use mbedtls +AM_CONDITIONAL(MBEDTLS, [test "$enable_mbedtls" != "no" && test "$have_mbedtls" = "yes"]) + dnl enable some warnings by default AX_CHECK_COMPILE_FLAG([-Wall], [CFLAGS="$CFLAGS -Wall"],[],[-Werror]) @@ -961,5 +968,5 @@ DX_INIT_DOXYGEN([libevent], [${top_srcdir}/Doxyfile], [doxygen]) AM_CONDITIONAL([ENABLE_DOXYGEN], [test "$DX_FLAG_doc" = "1"]) AM_CONDITIONAL([ENABLE_DOXYGEN_MAN], [test "$DX_FLAG_man" = "1"]) -AC_CONFIG_FILES( [libevent.pc libevent_openssl.pc libevent_pthreads.pc libevent_core.pc libevent_extra.pc] ) +AC_CONFIG_FILES( [libevent.pc libevent_mbedtls.pc libevent_openssl.pc libevent_pthreads.pc libevent_core.pc libevent_extra.pc] ) AC_OUTPUT(Makefile) diff --git a/event-config.h.cmake b/event-config.h.cmake index b8f59fef..1e7adb1c 100644 --- a/event-config.h.cmake +++ b/event-config.h.cmake @@ -214,6 +214,9 @@ /* Define if the system has openssl */ #cmakedefine EVENT__HAVE_OPENSSL 1 +/* Define if the system has mbedtls */ +#cmakedefine EVENT__HAVE_MBEDTLS 1 + /* Define to 1 if you have the `pipe' function. */ #cmakedefine EVENT__HAVE_PIPE 1 diff --git a/include/event2/bufferevent_ssl.h b/include/event2/bufferevent_ssl.h index 00e64071..69fb40b7 100644 --- a/include/event2/bufferevent_ssl.h +++ b/include/event2/bufferevent_ssl.h @@ -194,7 +194,7 @@ bufferevent_mbedtls_get_ssl(struct bufferevent *bufev); /** Tells a bufferevent to begin SSL renegotiation. */ EVENT2_EXPORT_SYMBOL -int bufferevent_ssl_renegotiate(struct bufferevent *bev); +int bufferevent_mbedtls_renegotiate(struct bufferevent *bev); /** Return the most recent OpenSSL error reported on an SSL bufferevent. */ EVENT2_EXPORT_SYMBOL diff --git a/include/event2/visibility.h b/include/event2/visibility.h index 006bbf06..2b07994a 100644 --- a/include/event2/visibility.h +++ b/include/event2/visibility.h @@ -33,7 +33,8 @@ defined(event_extra_shared_EXPORTS) || \ defined(event_core_shared_EXPORTS) || \ defined(event_pthreads_shared_EXPORTS) || \ - defined(event_openssl_shared_EXPORTS) + defined(event_openssl_shared_EXPORTS) || \ + defined(event_mbedtls_shared_EXPORTS) # if defined (__SUNPRO_C) && (__SUNPRO_C >= 0x550) # define EVENT2_EXPORT_SYMBOL __global diff --git a/m4/libevent_mbedtls.m4 b/m4/libevent_mbedtls.m4 index 946a9fdd..4b903b9b 100644 --- a/m4/libevent_mbedtls.m4 +++ b/m4/libevent_mbedtls.m4 @@ -11,18 +11,15 @@ case "$enable_mbedtls" in save_LIBS="$LIBS" LIBS="" MBEDTLS_LIBS="" - for lib in mbedtls ; do - # clear cache - unset ac_cv_search_mbedtls_ssl_init - AC_SEARCH_LIBS([mbedtls_ssl_init], [mbedtls ], - [have_mbedtls=yes - MBEDTLS_LIBS="$LIBS -l$lib -lmbedcrypto -lmbedx509 $EV_LIB_GDI $EV_LIB_WS32 $MBEDTLS_LIBADD"], - [have_mbedtls=no], - [-l$lib $EV_LIB_GDI $EV_LIB_WS32 $MBEDTLS_LIBADD]) - LIBS="$save_LIBS" - test "$have_mbedtls" = "yes" && break - done - ;; + # clear cache + unset ac_cv_search_mbedtls_ssl_init + AC_SEARCH_LIBS([mbedtls_ssl_init], [mbedtls], + [have_mbedtls=yes + MBEDTLS_LIBS="$LIBS -lmbedtls -lmbedcrypto -lmbedx509 $EV_LIB_GDI $EV_LIB_WS32"], + [have_mbedtls=no], + [-lmbedtls -lmbedcrypto -lmbedx509 $EV_LIB_GDI $EV_LIB_WS32]) + LIBS="$save_LIBS" + test "$have_mbedtls" = "yes" && break esac CPPFLAGS_SAVE=$CPPFLAGS CPPFLAGS="$CPPFLAGS $MBEDTLS_INCS" diff --git a/sample/include.am b/sample/include.am index 301787db..19d6a9a6 100644 --- a/sample/include.am +++ b/sample/include.am @@ -40,6 +40,13 @@ noinst_HEADERS += \ sample/openssl_hostname_validation.h endif +if MBEDTLS +SAMPLES += sample/ssl-client-mbedtls +sample_ssl_client_mbedtls_SOURCES = sample/ssl-client-mbedtls.c +sample_ssl_client_mbedtls_LDADD = libevent.la libevent_mbedtls.la $(MBEDTLS_LIBS) $(MBEDTLS_LIBADD) +sample_ssl_client_mbedtls_CPPFLAGS = $(AM_CPPFLAGS) $(MBEDTLS_INCS) +endif + if BUILD_SAMPLES noinst_PROGRAMS += $(SAMPLES) endif diff --git a/sample/ssl-client-mbedtls.c b/sample/ssl-client-mbedtls.c new file mode 100644 index 00000000..965913b2 --- /dev/null +++ b/sample/ssl-client-mbedtls.c @@ -0,0 +1,264 @@ +/* + * SSL client demonstration program + * + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +#include "mbedtls/config.h" +#include "mbedtls/platform.h" + +#include "mbedtls/net_sockets.h" +#include "mbedtls/debug.h" +#include "mbedtls/ssl.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/error.h" +#include "mbedtls/certs.h" + +#include + +#include +#include +#include +#include +#include + +#define SERVER_PORT "443" +#define SERVER_NAME "amazon.com" +#define GET_REQUEST "GET / HTTP/1.0\r\n\r\n" + +#define DEBUG_LEVEL 1 + +static void +my_debug(void *ctx, int level, const char *file, int line, const char *str) +{ + ((void)level); + + mbedtls_fprintf((FILE *)ctx, "%s:%04d: %s", file, line, str); + fflush((FILE *)ctx); +} + +static void +writecb(struct bufferevent *bev, void *arg) +{ + fprintf(stderr, "writecb\n"); +} + +static void +readcb(struct bufferevent *bev, void *arg) +{ + char buf[1000]; + size_t r = 0; + int i; + for (i = 0; i < 10; ++i) { + r = bufferevent_read(bev, buf, 800); + fprintf(stderr, "readcb %zu\n\n", r); + if (r > 1) { + fwrite(buf, 1, r, stdout); + fwrite("\n", 1, r, stdout); + fflush(stdout); + } + } +} + +static void +eventcb(struct bufferevent *bev, short what, void *arg) +{ + fprintf(stderr, "\n---------------eventcb %d\n", what); + if (what & BEV_EVENT_CONNECTED) { + const char headers[] = "GET / HTTP/1.1\r\n" + "HOST: " SERVER_NAME "\r\n" + "User-Agent: curl/7.65.1\r\n" + "Connection: Keep-Alive\r\n" + "\r\n"; + bufferevent_write( + bev, headers, sizeof(headers) - 1); // without ending '\0' + // bufferevent_disable(bev, EV_WRITE); + fprintf(stderr, "write request completely\n"); + } else if (what & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) { + fprintf(stderr, "closed\n"); + bufferevent_free(bev); + } +} + + +int +main(void) +{ + int ret; + mbedtls_net_context server_fd; + const char *pers = "ssl_client1"; + + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ssl_context ssl; + mbedtls_ssl_config conf; + mbedtls_x509_crt cacert; + + struct event_base *evbase; + struct evdns_base *evdns; + struct bufferevent *bev; + struct bufferevent *bevf; + +#ifdef WIN32 + WORD wVersionRequested; + WSADATA wsaData; + int err; + + /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */ + wVersionRequested = MAKEWORD(2, 2); + + err = WSAStartup(wVersionRequested, &wsaData); +#endif + + +#if defined(MBEDTLS_DEBUG_C) + mbedtls_debug_set_threshold(DEBUG_LEVEL); +#endif + + /* + * 0. Initialize the RNG and the session data + */ + mbedtls_net_init(&server_fd); + mbedtls_ssl_init(&ssl); + mbedtls_ssl_config_init(&conf); + mbedtls_x509_crt_init(&cacert); + mbedtls_ctr_drbg_init(&ctr_drbg); + + mbedtls_printf("\n . Seeding the random number generator..."); + fflush(stdout); + + mbedtls_entropy_init(&entropy); + if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, + (const unsigned char *)pers, strlen(pers))) != 0) { + mbedtls_printf(" failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret); + goto exit; + } + + mbedtls_printf(" ok\n"); + + /* + * 0. Initialize certificates + */ + mbedtls_printf(" . Loading the CA root certificate ..."); + fflush(stdout); + + ret = mbedtls_x509_crt_parse(&cacert, + (const unsigned char *)mbedtls_test_cas_pem, mbedtls_test_cas_pem_len); + if (ret < 0) { + mbedtls_printf( + " failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n", -ret); + goto exit; + } + + mbedtls_printf(" ok (%d skipped)\n", ret); + + /* + * 1. Start the connection + */ + mbedtls_printf(" . Connecting to tcp/%s/%s...", SERVER_NAME, SERVER_PORT); + fflush(stdout); + + if ((ret = mbedtls_net_connect(&server_fd, SERVER_NAME, SERVER_PORT, + MBEDTLS_NET_PROTO_TCP)) != 0) { + mbedtls_printf(" failed\n ! mbedtls_net_connect returned %d\n\n", ret); + goto exit; + } + + mbedtls_printf(" ok\n"); + + /* + * 2. Setup stuff + */ + mbedtls_printf(" . Setting up the SSL/TLS structure..."); + fflush(stdout); + + if ((ret = mbedtls_ssl_config_defaults(&conf, MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { + mbedtls_printf( + " failed\n ! mbedtls_ssl_config_defaults returned %d\n\n", ret); + goto exit; + } + + mbedtls_printf(" ok\n"); + + /* OPTIONAL is not optimal for security, + * but makes interop easier in this simplified example */ + mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_NONE); + mbedtls_ssl_conf_ca_chain(&conf, &cacert, NULL); + mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg); + mbedtls_ssl_conf_dbg(&conf, my_debug, stdout); + + if ((ret = mbedtls_ssl_setup(&ssl, &conf)) != 0) { + mbedtls_printf(" failed\n ! mbedtls_ssl_setup returned %d\n\n", ret); + goto exit; + } + + if ((ret = mbedtls_ssl_set_hostname(&ssl, SERVER_NAME)) != 0) { + mbedtls_printf( + " failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", ret); + goto exit; + } + fflush(stdout); + + event_enable_debug_mode(); + evbase = event_base_new(); + evdns = evdns_base_new(evbase, 1); + evdns_base_set_option(evdns, "randomize-case:", "0"); + + evutil_make_socket_nonblocking(server_fd.fd); + + bev = bufferevent_socket_new(evbase, server_fd.fd, BEV_OPT_CLOSE_ON_FREE); + bevf = bufferevent_mbedtls_filter_new( + evbase, bev, &ssl, BUFFEREVENT_SSL_CONNECTING, BEV_OPT_CLOSE_ON_FREE); + bev = bevf; + bufferevent_setcb(bev, readcb, writecb, eventcb, NULL); + + bufferevent_enable(bev, EV_READ); + + + event_base_loop(evbase, 0); + event_base_free(evbase); + + +exit: + +#ifdef MBEDTLS_ERROR_C + if (ret != 0) { + char error_buf[100]; + mbedtls_strerror(ret, error_buf, 100); + mbedtls_printf("Last error was: %d - %s\n\n", ret, error_buf); + } +#endif + + mbedtls_net_free(&server_fd); + + mbedtls_x509_crt_free(&cacert); + mbedtls_ssl_free(&ssl); + mbedtls_ssl_config_free(&conf); + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + +#if defined(_WIN32) + mbedtls_printf(" + Press Enter to exit this program.\n"); + fflush(stdout); + getchar(); +#endif + + return (ret); +} diff --git a/sample/ssl_client1.c b/sample/ssl_client1.c deleted file mode 100644 index af8019c9..00000000 --- a/sample/ssl_client1.c +++ /dev/null @@ -1,315 +0,0 @@ -/* - * SSL client demonstration program - * - * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This file is part of mbed TLS (https://tls.mbed.org) - */ - -#if !defined(MBEDTLS_CONFIG_FILE) -#include "mbedtls/config.h" -#else -#include MBEDTLS_CONFIG_FILE -#endif - -#if defined(MBEDTLS_PLATFORM_C) -#include "mbedtls/platform.h" -#else -#include -#include -#define mbedtls_time time -#define mbedtls_time_t time_t -#define mbedtls_fprintf fprintf -#define mbedtls_printf printf -#endif - -#if !defined(MBEDTLS_BIGNUM_C) || !defined(MBEDTLS_ENTROPY_C) || \ - !defined(MBEDTLS_SSL_TLS_C) || !defined(MBEDTLS_SSL_CLI_C) || \ - !defined(MBEDTLS_NET_C) || !defined(MBEDTLS_RSA_C) || \ - !defined(MBEDTLS_CERTS_C) || !defined(MBEDTLS_PEM_PARSE_C) || \ - !defined(MBEDTLS_CTR_DRBG_C) || !defined(MBEDTLS_X509_CRT_PARSE_C) -int main( void ) -{ - mbedtls_printf("MBEDTLS_BIGNUM_C and/or MBEDTLS_ENTROPY_C and/or " - "MBEDTLS_SSL_TLS_C and/or MBEDTLS_SSL_CLI_C and/or " - "MBEDTLS_NET_C and/or MBEDTLS_RSA_C and/or " - "MBEDTLS_CTR_DRBG_C and/or MBEDTLS_X509_CRT_PARSE_C " - "not defined.\n"); - return( 0 ); -} -#else - -#include "mbedtls/net_sockets.h" -#include "mbedtls/debug.h" -#include "mbedtls/ssl.h" -#include "mbedtls/entropy.h" -#include "mbedtls/ctr_drbg.h" -#include "mbedtls/error.h" -#include "mbedtls/certs.h" - -#include - -#include -#include -#include -#include - -#define SERVER_PORT "443" -#define SERVER_NAME "amazon.com" -#define GET_REQUEST "GET / HTTP/1.0\r\n\r\n" - -#define DEBUG_LEVEL 1 - -static void my_debug( void *ctx, int level, - const char *file, int line, - const char *str ) -{ - ((void) level); - - mbedtls_fprintf( (FILE *) ctx, "%s:%04d: %s", file, line, str ); - fflush( (FILE *) ctx ); -} - -void writecb(struct bufferevent *bev, void *arg) -{ - fprintf(stderr, "writecb\n"); -} - -void readcb(struct bufferevent *bev, void *arg) -{ - char buf[1000] = {}; - size_t r = 0; - int i; - for (i=0; i<10; ++i) -{ - r = bufferevent_read(bev, buf, 800); - fprintf(stderr, "readcb %d\n\n", r); - if (r > 1) { - fwrite(buf, 1, r, stdout); - fwrite("\n", 1, r, stdout); - fflush(stdout); - } - } -} - -void eventcb(struct bufferevent *bev, short what, void *arg) -{ - fprintf(stderr, "\n---------------eventcb %d\n", what); - if (what & BEV_EVENT_CONNECTED) { - const char headers[] = - "GET / HTTP/1.1\r\n" - "HOST: " SERVER_NAME "\r\n" - "User-Agent: curl/7.65.1\r\n" - "Connection: Keep-Alive\r\n" - "\r\n"; - bufferevent_write(bev, headers, sizeof(headers) - 1); // without ending '\0' - //bufferevent_disable(bev, EV_WRITE); - fprintf(stderr, "write request completely\n"); - } else if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) { - fprintf(stderr, "closed\n"); - bufferevent_free(bev); - } -} - - - -int main( void ) -{ - int ret, len; - mbedtls_net_context server_fd; - uint32_t flags; - unsigned char buf[1024]; - const char *pers = "ssl_client1"; - - mbedtls_entropy_context entropy; - mbedtls_ctr_drbg_context ctr_drbg; - mbedtls_ssl_context ssl; - mbedtls_ssl_config conf; - mbedtls_x509_crt cacert; - - struct event *ev_sigterm; - struct event_base *evbase; - struct evdns_base *evdns; - -#ifdef WIN32 - WORD wVersionRequested; - WSADATA wsaData; - int err; - - /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */ - wVersionRequested = MAKEWORD(2, 2); - - err = WSAStartup(wVersionRequested, &wsaData); -#endif - - -#if defined(MBEDTLS_DEBUG_C) - mbedtls_debug_set_threshold( DEBUG_LEVEL ); -#endif - - /* - * 0. Initialize the RNG and the session data - */ - mbedtls_net_init( &server_fd ); - mbedtls_ssl_init( &ssl ); - mbedtls_ssl_config_init( &conf ); - mbedtls_x509_crt_init( &cacert ); - mbedtls_ctr_drbg_init( &ctr_drbg ); - - mbedtls_printf( "\n . Seeding the random number generator..." ); - fflush( stdout ); - - mbedtls_entropy_init( &entropy ); - if( ( ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy, - (const unsigned char *) pers, - strlen( pers ) ) ) != 0 ) - { - mbedtls_printf( " failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret ); - goto exit; - } - - mbedtls_printf( " ok\n" ); - - /* - * 0. Initialize certificates - */ - mbedtls_printf( " . Loading the CA root certificate ..." ); - fflush( stdout ); - - ret = mbedtls_x509_crt_parse( &cacert, (const unsigned char *) mbedtls_test_cas_pem, - mbedtls_test_cas_pem_len ); - if( ret < 0 ) - { - mbedtls_printf( " failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n", -ret ); - goto exit; - } - - mbedtls_printf( " ok (%d skipped)\n", ret ); - - /* - * 1. Start the connection - */ - mbedtls_printf( " . Connecting to tcp/%s/%s...", SERVER_NAME, SERVER_PORT ); - fflush( stdout ); - - if( ( ret = mbedtls_net_connect( &server_fd, SERVER_NAME, - SERVER_PORT, MBEDTLS_NET_PROTO_TCP ) ) != 0 ) - { - mbedtls_printf( " failed\n ! mbedtls_net_connect returned %d\n\n", ret ); - goto exit; - } - - mbedtls_printf( " ok\n" ); - - /* - * 2. Setup stuff - */ - mbedtls_printf( " . Setting up the SSL/TLS structure..." ); - fflush( stdout ); - - if( ( ret = mbedtls_ssl_config_defaults( &conf, - MBEDTLS_SSL_IS_CLIENT, - MBEDTLS_SSL_TRANSPORT_STREAM, - MBEDTLS_SSL_PRESET_DEFAULT ) ) != 0 ) - { - mbedtls_printf( " failed\n ! mbedtls_ssl_config_defaults returned %d\n\n", ret ); - goto exit; - } - - mbedtls_printf( " ok\n" ); - - /* OPTIONAL is not optimal for security, - * but makes interop easier in this simplified example */ - mbedtls_ssl_conf_authmode( &conf, MBEDTLS_SSL_VERIFY_NONE ); - mbedtls_ssl_conf_ca_chain( &conf, &cacert, NULL ); - mbedtls_ssl_conf_rng( &conf, mbedtls_ctr_drbg_random, &ctr_drbg ); - mbedtls_ssl_conf_dbg( &conf, my_debug, stdout ); - - if( ( ret = mbedtls_ssl_setup( &ssl, &conf ) ) != 0 ) - { - mbedtls_printf( " failed\n ! mbedtls_ssl_setup returned %d\n\n", ret ); - goto exit; - } - - if( ( ret = mbedtls_ssl_set_hostname( &ssl, SERVER_NAME ) ) != 0 ) - { - mbedtls_printf( " failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", ret ); - goto exit; - } - fflush( stdout ); - - //mbedtls_ssl_set_bio( &ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, NULL ); - - event_enable_debug_mode(); - evbase = event_base_new(); - evdns = evdns_base_new(evbase, 1); - evdns_base_set_option(evdns, "randomize-case:", "0"); - - evutil_make_socket_nonblocking(server_fd.fd); - - -#if 1 -struct bufferevent *bev = bufferevent_socket_new(evbase, server_fd.fd, BEV_OPT_CLOSE_ON_FREE); -struct bufferevent *bevf = bufferevent_mbedtls_filter_new( - evbase, bev, &ssl, - BUFFEREVENT_SSL_CONNECTING, BEV_OPT_CLOSE_ON_FREE); - bev = bevf; -#else - -struct bufferevent *bev = bufferevent_mbedtls_socket_new( - evbase, server_fd.fd, &ssl, - BUFFEREVENT_SSL_CONNECTING, BEV_OPT_CLOSE_ON_FREE); -#endif - bufferevent_setcb(bev, readcb, NULL, eventcb, NULL); - - bufferevent_enable(bev, EV_READ); - - - event_base_loop(evbase, 0); - event_base_free(evbase); - - - -exit: - -#ifdef MBEDTLS_ERROR_C - if( ret != 0 ) - { - char error_buf[100]; - mbedtls_strerror( ret, error_buf, 100 ); - mbedtls_printf("Last error was: %d - %s\n\n", ret, error_buf ); - } -#endif - - mbedtls_net_free( &server_fd ); - - mbedtls_x509_crt_free( &cacert ); - mbedtls_ssl_free( &ssl ); - mbedtls_ssl_config_free( &conf ); - mbedtls_ctr_drbg_free( &ctr_drbg ); - mbedtls_entropy_free( &entropy ); - -#if defined(_WIN32) - mbedtls_printf( " + Press Enter to exit this program.\n" ); - fflush( stdout ); getchar(); -#endif - - return( ret ); -} -#endif /* MBEDTLS_BIGNUM_C && MBEDTLS_ENTROPY_C && MBEDTLS_SSL_TLS_C && - MBEDTLS_SSL_CLI_C && MBEDTLS_NET_C && MBEDTLS_RSA_C && - MBEDTLS_CERTS_C && MBEDTLS_PEM_PARSE_C && MBEDTLS_CTR_DRBG_C && - MBEDTLS_X509_CRT_PARSE_C */ diff --git a/test-export/test-export.c b/test-export/test-export.c index 90917775..3dbeadb5 100644 --- a/test-export/test-export.c +++ b/test-export/test-export.c @@ -9,6 +9,9 @@ #include #include #include +#elif defined(EVENT_EXPORT_TEST_COMPONENT_MBEDTLS) +#include +#include #endif #if defined(EVENT_EXPORT_TEST_COMPONENT_EXTRA) @@ -90,6 +93,54 @@ error: SSL_free(ssl); return r; } +#elif defined(EVENT_EXPORT_TEST_COMPONENT_MBEDTLS) +static int +test() +{ + struct event_base *base = NULL; + mbedtls_ssl_config *conf = NULL; + mbedtls_ssl_context *ssl = NULL; + struct bufferevent *bev; + int r = 1; + + base = event_base_new(); + if (!base) { + goto error; + } + + conf = malloc(sizeof(*conf)); + if (!conf) { + goto error; + } + mbedtls_ssl_config_init(conf); + + ssl = malloc(sizeof(*ssl)); + if (!ssl) { + goto error; + } + mbedtls_ssl_init(ssl); + mbedtls_ssl_setup(ssl, conf); + + bev = bufferevent_mbedtls_socket_new(base, -1, ssl, + BUFFEREVENT_SSL_CONNECTING, + BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS); + if (bev == NULL) { + goto error; + } + r = 0; +error: + if (base) + event_base_free(base); + if (ssl) { + mbedtls_ssl_free(ssl); + free(ssl); + } + if (conf) { + mbedtls_ssl_config_free(conf); + free(conf); + } + return r; +} #else static int test() diff --git a/test-export/test-export.py b/test-export/test-export.py index d71e5dba..a6def1ca 100644 --- a/test-export/test-export.py +++ b/test-export/test-export.py @@ -88,13 +88,18 @@ def test_group(): testcase("core", "core", 0) testcase("extra", "extra", 0) testcase("openssl", "openssl", 0) + testcase("mbedtls", "mbedtls", 0) testcase("", "", 0) testcase("extra", "core", 0) testcase("openssl", "core", 0) + testcase("mbedtls", "core", 0) testcase("core", "extra", 1) testcase("core", "openssl", 1) testcase("extra", "openssl", 1) testcase("openssl", "extra", 1) + testcase("core", "mbedtls", 1) + testcase("extra", "mbedtls", 1) + testcase("mbedtls", "extra", 1) if platform.system() != "Windows": testcase("pthreads", "pthreads", 0) testcase("pthreads", "core", 0) @@ -103,6 +108,8 @@ def test_group(): testcase("pthreads", "extra", 1) testcase("pthreads", "openssl", 1) testcase("openssl", "pthreads", 1) + testcase("pthreads", "mbedtls", 1) + testcase("mbedtls", "pthreads", 1) def config_restore(): diff --git a/test/include.am b/test/include.am index 50e9cdbd..8ec8d534 100644 --- a/test/include.am +++ b/test/include.am @@ -149,11 +149,17 @@ test_regress_CPPFLAGS = $(AM_CPPFLAGS) $(PTHREAD_CFLAGS) $(ZLIB_CFLAGS) -Itest test_regress_LDFLAGS = $(PTHREAD_CFLAGS) if OPENSSL -test_regress_SOURCES += test/regress_ssl.c +test_regress_SOURCES += test/regress_openssl.c test_regress_CPPFLAGS += $(OPENSSL_INCS) test_regress_LDADD += libevent_openssl.la $(OPENSSL_LIBS) ${OPENSSL_LIBADD} endif +if MBEDTLS +test_regress_SOURCES += test/regress_mbedtls.c +test_regress_CPPFLAGS += $(MBEDTLS_INCS) +test_regress_LDADD += libevent_mbedtls.la $(MBEDTLS_LIBS) +endif + test_bench_SOURCES = test/bench.c test_bench_LDADD = $(LIBEVENT_GC_SECTIONS) libevent.la test_bench_cascade_SOURCES = test/bench_cascade.c diff --git a/test/regress.h b/test/regress.h index 55a2fddb..35965715 100644 --- a/test/regress.h +++ b/test/regress.h @@ -50,6 +50,7 @@ extern struct testcase_t edgetriggered_testcases[]; extern struct testcase_t minheap_testcases[]; extern struct testcase_t iocp_testcases[]; extern struct testcase_t ssl_testcases[]; +extern struct testcase_t mbedtls_testcases[]; extern struct testcase_t listener_testcases[]; extern struct testcase_t listener_iocp_testcases[]; extern struct testcase_t thread_testcases[]; diff --git a/test/regress_main.c b/test/regress_main.c index 2951e3d4..35945938 100644 --- a/test/regress_main.c +++ b/test/regress_main.c @@ -452,6 +452,9 @@ struct testgroup_t testgroups[] = { #endif #ifdef EVENT__HAVE_OPENSSL { "ssl/", ssl_testcases }, +#endif +#ifdef EVENT__HAVE_MBEDTLS + { "mbedtls/", mbedtls_testcases }, #endif END_OF_GROUPS }; diff --git a/test/regress_mbedtls.c b/test/regress_mbedtls.c new file mode 100644 index 00000000..44434fe4 --- /dev/null +++ b/test/regress_mbedtls.c @@ -0,0 +1,335 @@ +/* + * 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. + */ +/** For event_debug() usage/coverage */ +#define EVENT_VISIBILITY_WANT_DLLIMPORT + +#include "event2/util.h" +#include +#include +#include +#include +#include "regress.h" +#include "tinytest.h" + +#define TESTCASES_NAME mbedtls_testcases + +#ifdef OPENSSL_VERSION_NUMBER +#undef OPENSSL_VERSION_NUMBER +#endif +#define OPENSSL_VERSION_NUMBER 0 +#define SSL_IS_CLIENT MBEDTLS_SSL_IS_CLIENT +#define SSL_IS_SERVER MBEDTLS_SSL_IS_SERVER + +#define get_ssl_ctx get_mbedtls_config + +#define SSL_renegotiate mbedtls_ssl_renegotiate +#define SSL_get_peer_certificate mbedtls_ssl_get_peer_cert +#define SSL_new mbedtls_ssl_new +#define SSL_use_certificate(a, b) \ + do { \ + } while (0); +#define SSL_use_PrivateKey(a, b) \ + do { \ + } while (0); +#define X509_free(x) \ + do { \ + } while (0); + +#define X509 const mbedtls_x509_crt +#define SSL mbedtls_ssl_context + +#define bufferevent_ssl_get_ssl bufferevent_mbedtls_get_ssl +#define bufferevent_ssl_set_allow_dirty_shutdown \ + bufferevent_mbedtls_set_allow_dirty_shutdown +#define bufferevent_ssl_socket_new bufferevent_mbedtls_socket_new +#define bufferevent_ssl_filter_new bufferevent_mbedtls_filter_new + +struct rwcount; +static void BIO_setup(SSL *ssl, struct rwcount *rw); +static mbedtls_ssl_config *get_mbedtls_config(int endpoint); +static mbedtls_ssl_context *mbedtls_ssl_new(mbedtls_ssl_config *config); +static void *mbedtls_test_setup(const struct testcase_t *testcase); +static int mbedtls_test_cleanup(const struct testcase_t *testcase, void *ptr); +static const struct testcase_setup_t ssl_setup = { + mbedtls_test_setup, mbedtls_test_cleanup}; +#include "regress_ssl.c" +static mbedtls_ssl_config *the_mbedtls_conf[2] = {NULL, NULL}; +static mbedtls_ssl_context *the_mbedtls_ctx[1024] = {NULL}; +static int the_mbedtls_ctx_count = 0; +static mbedtls_entropy_context entropy; +static mbedtls_ctr_drbg_context ctr_drbg; +static mbedtls_x509_crt *the_cert; +static mbedtls_pk_context *the_key; + +static void +mbedtls_debug( + void *userdata, int level, const char *file, int line, const char *str) +{ + int loglen = strlen(str); + if (str[loglen - 1] == '\n') + loglen--; + event_debug(("[mbedtls][%s][%d][%s][%d]%.*s", (char *)userdata, level, file, + line, loglen, str)); +} + +static mbedtls_pk_context * +mbedtls_getkey(void) +{ + int ret = 0; + mbedtls_pk_context *pk = malloc(sizeof(mbedtls_pk_context)); + tt_assert(pk); + mbedtls_pk_init(pk); + ret = mbedtls_pk_parse_key( + pk, (const unsigned char *)KEY, sizeof(KEY), NULL, 0); + tt_assert(ret == 0); + return pk; +end: + if (pk) { + mbedtls_pk_free(pk); + free(pk); + } + return NULL; +} + +static void +create_tm_from_unix_epoch(struct tm *cur_p, const time_t t) +{ +#ifdef _WIN32 + struct tm *tmp = gmtime(&t); + if (!tmp) { + fprintf(stderr, "gmtime: %s (%i)", strerror(errno), (int)t); + exit(1); + } + *cur_p = *tmp; +#else + gmtime_r(&t, cur_p); +#endif +} + +static mbedtls_x509_crt * +mbedtls_getcert(mbedtls_pk_context *pk) +{ + const char *name = "commonName=example.com"; + time_t now = time(NULL); + char now_string[32] = ""; + char not_before[32] = ""; + char not_after[32] = ""; + unsigned char certbuf[8192]; + struct tm tm; + mbedtls_x509_crt *crt = NULL; + int ret = 0; + + mbedtls_mpi serial; + mbedtls_x509write_cert write_cert; + + snprintf(now_string, sizeof(now_string), "%lld", (long long)now); + + create_tm_from_unix_epoch(&tm, now); + strftime(not_before, sizeof(not_before), "%Y%m%d%H%M%S", &tm); + now += 3600; + create_tm_from_unix_epoch(&tm, now); + strftime(not_after, sizeof(not_after), "%Y%m%d%H%M%S", &tm); + + mbedtls_x509write_crt_init(&write_cert); + mbedtls_x509write_crt_set_version(&write_cert, 2); + + mbedtls_mpi_init(&serial); + ret = mbedtls_mpi_read_string(&serial, 10, now_string); + tt_assert(ret == 0); + ret = mbedtls_x509write_crt_set_serial(&write_cert, &serial); + tt_assert(ret == 0); + mbedtls_mpi_free(&serial); + + ret = mbedtls_x509write_crt_set_subject_name(&write_cert, name); + tt_assert(ret == 0); + ret = mbedtls_x509write_crt_set_issuer_name(&write_cert, name); + tt_assert(ret == 0); + + mbedtls_x509write_crt_set_md_alg(&write_cert, MBEDTLS_MD_SHA256); + + ret = + mbedtls_x509write_crt_set_validity(&write_cert, not_before, not_after); + tt_assert(ret == 0); + mbedtls_x509write_crt_set_issuer_key(&write_cert, pk); + mbedtls_x509write_crt_set_subject_key(&write_cert, pk); + + ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, + (const unsigned char *)name, strlen(name)); + tt_assert(ret == 0); + ret = mbedtls_x509write_crt_pem(&write_cert, certbuf, sizeof(certbuf), + mbedtls_ctr_drbg_random, &ctr_drbg); + tt_assert(ret == 0); + mbedtls_x509write_crt_free(&write_cert); + + crt = malloc(sizeof(mbedtls_x509_crt)); + tt_assert(crt); + mbedtls_x509_crt_init(crt); + ret = mbedtls_x509_crt_parse(crt, certbuf, strlen((char *)certbuf) + 1); + tt_assert(ret == 0); + return crt; +end: + if (crt) { + mbedtls_x509_crt_free(crt); + free(crt); + } + return NULL; +} + +static mbedtls_ssl_config * +get_mbedtls_config(int endpoint) +{ + if (the_mbedtls_conf[endpoint]) + return the_mbedtls_conf[endpoint]; + the_mbedtls_conf[endpoint] = malloc(sizeof(mbedtls_ssl_config)); + if (!the_mbedtls_conf[endpoint]) + return NULL; + mbedtls_ssl_config_init(the_mbedtls_conf[endpoint]); + mbedtls_ssl_conf_renegotiation( + the_mbedtls_conf[endpoint], MBEDTLS_SSL_RENEGOTIATION_ENABLED); + mbedtls_ssl_conf_dbg(the_mbedtls_conf[endpoint], mbedtls_debug, + (void *)(endpoint == MBEDTLS_SSL_IS_SERVER ? "server" : "client")); + mbedtls_ssl_config_defaults(the_mbedtls_conf[endpoint], endpoint, + MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); + mbedtls_ssl_conf_rng( + the_mbedtls_conf[endpoint], mbedtls_ctr_drbg_random, &ctr_drbg); + if (disable_tls_11_and_12) { + mbedtls_ssl_conf_max_version(the_mbedtls_conf[endpoint], + MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_1); + } + if (endpoint == MBEDTLS_SSL_IS_SERVER) { + mbedtls_ssl_conf_own_cert( + the_mbedtls_conf[endpoint], the_cert, the_key); + } else { /* MBEDTLS_SSL_IS_CLIENT */ + mbedtls_ssl_conf_ca_chain(the_mbedtls_conf[endpoint], the_cert, NULL); + } + return the_mbedtls_conf[endpoint]; +} + +static void +init_mbedtls(void) +{ + mbedtls_debug_set_threshold(5); +} + +static void * +mbedtls_test_setup(const struct testcase_t *testcase) +{ + init_mbedtls(); + + mbedtls_entropy_init(&entropy); + mbedtls_ctr_drbg_init(&ctr_drbg); + mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, + (const unsigned char *)"libevent", sizeof("libevent")); + + the_key = mbedtls_getkey(); + EVUTIL_ASSERT(the_key); + + the_cert = mbedtls_getcert(the_key); + EVUTIL_ASSERT(the_cert); + + disable_tls_11_and_12 = 0; + + return basic_test_setup(testcase); +} +static int +mbedtls_test_cleanup(const struct testcase_t *testcase, void *ptr) +{ + int i; + int ret = basic_test_cleanup(testcase, ptr); + if (!ret) { + return ret; + } + + test_is_done = 0; + n_connected = 0; + got_close = 0; + got_error = 0; + got_timeout = 0; + renegotiate_at = -1; + stop_when_connected = 0; + pending_connect_events = 0; + exit_base = NULL; + + mbedtls_x509_crt_free(the_cert); + free(the_cert); + mbedtls_pk_free(the_key); + free(the_key); + + for (i = 0; i < the_mbedtls_ctx_count; i++) { + mbedtls_ssl_free(the_mbedtls_ctx[i]); + } + if (the_mbedtls_conf[0]) { + mbedtls_ssl_config_free(the_mbedtls_conf[0]); + free(the_mbedtls_conf[0]); + the_mbedtls_conf[0] = NULL; + } + if (the_mbedtls_conf[1]) { + mbedtls_ssl_config_free(the_mbedtls_conf[1]); + free(the_mbedtls_conf[1]); + the_mbedtls_conf[1] = NULL; + } + + return 1; +} + +static mbedtls_ssl_context * +mbedtls_ssl_new(mbedtls_ssl_config *config) +{ + mbedtls_ssl_context *ssl = malloc(sizeof(*ssl)); + mbedtls_ssl_init(ssl); + mbedtls_ssl_setup(ssl, config); + the_mbedtls_ctx[the_mbedtls_ctx_count++] = ssl; + return ssl; +} + +static int +bio_rwcount_read(void *ctx, unsigned char *out, size_t outlen) +{ + struct rwcount *rw = ctx; + ev_ssize_t ret = recv(rw->fd, out, outlen, 0); + ++rw->read; + if (ret == -1 && EVUTIL_ERR_RW_RETRIABLE(EVUTIL_SOCKET_ERROR())) { + return MBEDTLS_ERR_SSL_WANT_READ; + } + return ret; +} +static int +bio_rwcount_write(void *ctx, const unsigned char *in, size_t inlen) +{ + struct rwcount *rw = ctx; + ev_ssize_t ret = send(rw->fd, in, inlen, 0); + ++rw->write; + if (ret == -1 && EVUTIL_ERR_RW_RETRIABLE(EVUTIL_SOCKET_ERROR())) { + return MBEDTLS_ERR_SSL_WANT_WRITE; + } + return ret; +} +static void +BIO_setup(SSL *ssl, struct rwcount *rw) +{ + mbedtls_ssl_set_bio(ssl, rw, bio_rwcount_write, bio_rwcount_read, + NULL); +} diff --git a/test/regress_openssl.c b/test/regress_openssl.c new file mode 100644 index 00000000..1a0ddd0e --- /dev/null +++ b/test/regress_openssl.c @@ -0,0 +1,311 @@ +/* + * 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. + */ +#include "event2/util.h" +#include +#include +#include "openssl-compat.h" +#include "regress.h" +#include "tinytest.h" +#define TESTCASES_NAME ssl_testcases +static void *ssl_test_setup(const struct testcase_t *testcase); +static int ssl_test_cleanup(const struct testcase_t *testcase, void *ptr); +static const struct testcase_setup_t ssl_setup = { + ssl_test_setup, ssl_test_cleanup}; + +static X509 *the_cert; +EVP_PKEY *the_key; + +#define SSL_IS_CLIENT +#define SSL_IS_SERVER + +#define bufferevent_ssl_get_ssl bufferevent_openssl_get_ssl +#define bufferevent_ssl_set_allow_dirty_shutdown \ + bufferevent_openssl_set_allow_dirty_shutdown +#define bufferevent_ssl_socket_new bufferevent_openssl_socket_new +#define bufferevent_ssl_filter_new bufferevent_openssl_filter_new + +struct rwcount; +static void BIO_setup(SSL *ssl, struct rwcount *rw); +#include "regress_ssl.c" + +EVP_PKEY * +ssl_getkey(void) +{ + EVP_PKEY *key; + BIO *bio; + + /* new read-only BIO backed by KEY. */ + bio = BIO_new_mem_buf((char *)KEY, -1); + tt_assert(bio); + + key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + BIO_free(bio); + tt_assert(key); + + return key; +end: + return NULL; +} + +X509 * +ssl_getcert(EVP_PKEY *key) +{ + /* Dummy code to make a quick-and-dirty valid certificate with + OpenSSL. Don't copy this code into your own program! It does a + number of things in a stupid and insecure way. */ + X509 *x509 = NULL; + X509_NAME *name = NULL; + int nid; + time_t now = time(NULL); + + tt_assert(key); + + x509 = X509_new(); + tt_assert(x509); + tt_assert(0 != X509_set_version(x509, 2)); + tt_assert(0 != ASN1_INTEGER_set(X509_get_serialNumber(x509), (long)now)); + + name = X509_NAME_new(); + tt_assert(name); + nid = OBJ_txt2nid("commonName"); + tt_assert(NID_undef != nid); + tt_assert(0 != X509_NAME_add_entry_by_NID(name, nid, MBSTRING_ASC, + (unsigned char *)"example.com", -1, -1, 0)); + + X509_set_subject_name(x509, name); + X509_set_issuer_name(x509, name); + X509_NAME_free(name); + + X509_time_adj(X509_getm_notBefore(x509), 0, &now); + now += 3600; + X509_time_adj(X509_getm_notAfter(x509), 0, &now); + X509_set_pubkey(x509, key); + tt_assert(0 != X509_sign(x509, key, EVP_sha1())); + + return x509; +end: + X509_free(x509); + X509_NAME_free(name); + return NULL; +} + +static SSL_CTX *the_ssl_ctx = NULL; + +SSL_CTX * +get_ssl_ctx(void) +{ + if (the_ssl_ctx) + return the_ssl_ctx; + the_ssl_ctx = SSL_CTX_new(SSLv23_method()); + if (!the_ssl_ctx) + return NULL; + if (disable_tls_11_and_12) { +#ifdef SSL_OP_NO_TLSv1_2 + SSL_CTX_set_options(the_ssl_ctx, SSL_OP_NO_TLSv1_2); +#endif +#ifdef SSL_OP_NO_TLSv1_1 + SSL_CTX_set_options(the_ssl_ctx, SSL_OP_NO_TLSv1_1); +#endif + } + return the_ssl_ctx; +} + +void +init_ssl(void) +{ +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) + SSL_library_init(); + ERR_load_crypto_strings(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + if (SSLeay() != OPENSSL_VERSION_NUMBER) { + TT_DECLARE("WARN", ("Version mismatch for openssl: compiled with %lx " + "but running with %lx", + (unsigned long)OPENSSL_VERSION_NUMBER, + (unsigned long)SSLeay())); + } +#endif +} + +static void * +ssl_test_setup(const struct testcase_t *testcase) +{ + init_ssl(); + + the_key = ssl_getkey(); + EVUTIL_ASSERT(the_key); + + the_cert = ssl_getcert(the_key); + EVUTIL_ASSERT(the_cert); + + disable_tls_11_and_12 = 0; + + return basic_test_setup(testcase); +} +static int +ssl_test_cleanup(const struct testcase_t *testcase, void *ptr) +{ + int ret = basic_test_cleanup(testcase, ptr); + if (!ret) { + return ret; + } + + test_is_done = 0; + n_connected = 0; + got_close = 0; + got_error = 0; + got_timeout = 0; + renegotiate_at = -1; + stop_when_connected = 0; + pending_connect_events = 0; + exit_base = NULL; + + X509_free(the_cert); + EVP_PKEY_free(the_key); + + SSL_CTX_free(the_ssl_ctx); + the_ssl_ctx = NULL; + + return 1; +} + +static int +bio_rwcount_new(BIO *b) +{ + BIO_set_init(b, 0); + BIO_set_data(b, NULL); + return 1; +} +static int +bio_rwcount_free(BIO *b) +{ + TT_BLATHER(("bio_rwcount_free: %p", b)); + if (!b) + return 0; + if (BIO_get_shutdown(b)) { + BIO_set_init(b, 0); + BIO_set_data(b, NULL); + } + return 1; +} +static int +bio_rwcount_read(BIO *b, char *out, int outlen) +{ + struct rwcount *rw = BIO_get_data(b); + ev_ssize_t ret = recv(rw->fd, out, outlen, 0); + ++rw->read; + if (ret == -1 && EVUTIL_ERR_RW_RETRIABLE(EVUTIL_SOCKET_ERROR())) { + BIO_set_retry_read(b); + } + return ret; +} +static int +bio_rwcount_write(BIO *b, const char *in, int inlen) +{ + struct rwcount *rw = BIO_get_data(b); + ev_ssize_t ret = send(rw->fd, in, inlen, 0); + ++rw->write; + if (ret == -1 && EVUTIL_ERR_RW_RETRIABLE(EVUTIL_SOCKET_ERROR())) { + BIO_set_retry_write(b); + } + return ret; +} +static long +bio_rwcount_ctrl(BIO *b, int cmd, long num, void *ptr) +{ + struct rwcount *rw = BIO_get_data(b); + long ret = 0; + switch (cmd) { + case BIO_C_GET_FD: + ret = rw->fd; + break; + case BIO_CTRL_GET_CLOSE: + ret = BIO_get_shutdown(b); + break; + case BIO_CTRL_SET_CLOSE: + BIO_set_shutdown(b, (int)num); + break; + case BIO_CTRL_PENDING: + ret = 0; + break; + case BIO_CTRL_WPENDING: + ret = 0; + break; + case BIO_CTRL_DUP: + case BIO_CTRL_FLUSH: + ret = 1; + break; + } + return ret; +} +static int +bio_rwcount_puts(BIO *b, const char *s) +{ + return bio_rwcount_write(b, s, strlen(s)); +} +#define BIO_TYPE_LIBEVENT_RWCOUNT 0xff1 +static BIO_METHOD *methods_rwcount; + +static BIO_METHOD * +BIO_s_rwcount(void) +{ + if (methods_rwcount == NULL) { + methods_rwcount = BIO_meth_new(BIO_TYPE_LIBEVENT_RWCOUNT, "rwcount"); + if (methods_rwcount == NULL) + return NULL; + BIO_meth_set_write(methods_rwcount, bio_rwcount_write); + BIO_meth_set_read(methods_rwcount, bio_rwcount_read); + BIO_meth_set_puts(methods_rwcount, bio_rwcount_puts); + BIO_meth_set_ctrl(methods_rwcount, bio_rwcount_ctrl); + BIO_meth_set_create(methods_rwcount, bio_rwcount_new); + BIO_meth_set_destroy(methods_rwcount, bio_rwcount_free); + } + return methods_rwcount; +} +static BIO * +BIO_new_rwcount(int close_flag) +{ + BIO *result; + if (!(result = BIO_new(BIO_s_rwcount()))) + return NULL; + BIO_set_init(result, 1); + BIO_set_data(result, NULL); + BIO_set_shutdown(result, !!close_flag); + return result; +} +static void +BIO_setup(SSL *ssl, struct rwcount *rw) +{ + BIO *bio; + bio = BIO_new_rwcount(0); + tt_assert(bio); + BIO_set_data(bio, rw); + SSL_set_bio(ssl, bio, bio); +end: + return; +} diff --git a/test/regress_ssl.c b/test/regress_ssl.c index 37dc334d..b5c37b80 100644 --- a/test/regress_ssl.c +++ b/test/regress_ssl.c @@ -42,21 +42,15 @@ #include #endif -#include "event2/util.h" #include "event2/event.h" #include "event2/bufferevent_ssl.h" #include "event2/bufferevent_struct.h" #include "event2/buffer.h" #include "event2/listener.h" -#include "regress.h" #include "tinytest.h" #include "tinytest_macros.h" -#include -#include -#include "openssl-compat.h" - #include #ifdef _WIN32 #include @@ -98,91 +92,7 @@ static const char KEY[] = "lhdEOj7mAgHwGwwVZWOgs9Lq6vfztnSuhqjha1daESY6kDscPIQ=\n" "-----END RSA PRIVATE KEY-----\n"; -EVP_PKEY * -ssl_getkey(void) -{ - EVP_PKEY *key; - BIO *bio; - - /* new read-only BIO backed by KEY. */ - bio = BIO_new_mem_buf((char*)KEY, -1); - tt_assert(bio); - - key = PEM_read_bio_PrivateKey(bio,NULL,NULL,NULL); - BIO_free(bio); - tt_assert(key); - - return key; -end: - return NULL; -} - -X509 * -ssl_getcert(EVP_PKEY *key) -{ - /* Dummy code to make a quick-and-dirty valid certificate with - OpenSSL. Don't copy this code into your own program! It does a - number of things in a stupid and insecure way. */ - X509 *x509 = NULL; - X509_NAME *name = NULL; - int nid; - time_t now = time(NULL); - - tt_assert(key); - - x509 = X509_new(); - tt_assert(x509); - tt_assert(0 != X509_set_version(x509, 2)); - tt_assert(0 != ASN1_INTEGER_set(X509_get_serialNumber(x509), - (long)now)); - - name = X509_NAME_new(); - tt_assert(name); - nid = OBJ_txt2nid("commonName"); - tt_assert(NID_undef != nid); - tt_assert(0 != X509_NAME_add_entry_by_NID( - name, nid, MBSTRING_ASC, (unsigned char*)"example.com", - -1, -1, 0)); - - X509_set_subject_name(x509, name); - X509_set_issuer_name(x509, name); - X509_NAME_free(name); - - X509_time_adj(X509_getm_notBefore(x509), 0, &now); - now += 3600; - X509_time_adj(X509_getm_notAfter(x509), 0, &now); - X509_set_pubkey(x509, key); - tt_assert(0 != X509_sign(x509, key, EVP_sha1())); - - return x509; -end: - X509_free(x509); - X509_NAME_free(name); - return NULL; -} - static int disable_tls_11_and_12 = 0; -static SSL_CTX *the_ssl_ctx = NULL; - -SSL_CTX * -get_ssl_ctx(void) -{ - if (the_ssl_ctx) - return the_ssl_ctx; - the_ssl_ctx = SSL_CTX_new(SSLv23_method()); - if (!the_ssl_ctx) - return NULL; - if (disable_tls_11_and_12) { -#ifdef SSL_OP_NO_TLSv1_2 - SSL_CTX_set_options(the_ssl_ctx, SSL_OP_NO_TLSv1_2); -#endif -#ifdef SSL_OP_NO_TLSv1_1 - SSL_CTX_set_options(the_ssl_ctx, SSL_OP_NO_TLSv1_1); -#endif - } - return the_ssl_ctx; -} - static int test_is_done; static int n_connected; static int got_close; @@ -192,70 +102,6 @@ static int renegotiate_at = -1; static int stop_when_connected; static int pending_connect_events; static struct event_base *exit_base; -static X509 *the_cert; -EVP_PKEY *the_key; - -void -init_ssl(void) -{ -#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \ - (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L) - SSL_library_init(); - ERR_load_crypto_strings(); - SSL_load_error_strings(); - OpenSSL_add_all_algorithms(); - if (SSLeay() != OPENSSL_VERSION_NUMBER) { - TT_DECLARE("WARN", - ("Version mismatch for openssl: compiled with %lx but running with %lx", - (unsigned long)OPENSSL_VERSION_NUMBER, (unsigned long)SSLeay())); - } -#endif -} - -static void * -ssl_test_setup(const struct testcase_t *testcase) -{ - init_ssl(); - - the_key = ssl_getkey(); - EVUTIL_ASSERT(the_key); - - the_cert = ssl_getcert(the_key); - EVUTIL_ASSERT(the_cert); - - disable_tls_11_and_12 = 0; - - return basic_test_setup(testcase); -} -static int -ssl_test_cleanup(const struct testcase_t *testcase, void *ptr) -{ - int ret = basic_test_cleanup(testcase, ptr); - if (!ret) { - return ret; - } - - test_is_done = 0; - n_connected = 0; - got_close = 0; - got_error = 0; - got_timeout = 0; - renegotiate_at = -1; - stop_when_connected = 0; - pending_connect_events = 0; - exit_base = NULL; - - X509_free(the_cert); - EVP_PKEY_free(the_key); - - SSL_CTX_free(the_ssl_ctx); - the_ssl_ctx = NULL; - - return 1; -} -const struct testcase_setup_t ssl_setup = { - ssl_test_setup, ssl_test_cleanup -}; /* ==================== @@ -285,7 +131,7 @@ enum regress_openssl_type }; static void -bufferevent_openssl_check_fd(struct bufferevent *bev, int filter) +bufferevent_ssl_check_fd(struct bufferevent *bev, int filter) { tt_fd_op(bufferevent_getfd(bev), !=, EVUTIL_INVALID_SOCKET); tt_fd_op(bufferevent_setfd(bev, EVUTIL_INVALID_SOCKET), ==, 0); @@ -299,7 +145,7 @@ end: ; } static void -bufferevent_openssl_check_freed(struct bufferevent *bev) +bufferevent_ssl_check_freed(struct bufferevent *bev) { tt_int_op(event_pending(&bev->ev_read, EVLIST_ALL, NULL), ==, 0); tt_int_op(event_pending(&bev->ev_write, EVLIST_ALL, NULL), ==, 0); @@ -339,14 +185,15 @@ respond_to_number(struct bufferevent *bev, void *ctx) return; } if ((type & REGRESS_OPENSSL_CLIENT) && n == renegotiate_at) { - SSL_renegotiate(bufferevent_openssl_get_ssl(bev)); + SSL_renegotiate(bufferevent_ssl_get_ssl(bev)); } ++n; evbuffer_add_printf(bufferevent_get_output(bev), "%d\n", n); TT_BLATHER(("Done reading; now writing.")); bufferevent_enable(bev, EV_WRITE); - bufferevent_disable(bev, EV_READ); + // we shouldn't disable EV_READ here, otherwise we wouldn't got close cb + // bufferevent_disable(bev, EV_READ); } static void @@ -372,7 +219,7 @@ eventcb(struct bufferevent *bev, short what, void *ctx) if (what & BEV_EVENT_CONNECTED) { SSL *ssl; ++n_connected; - ssl = bufferevent_openssl_get_ssl(bev); + ssl = bufferevent_ssl_get_ssl(bev); tt_assert(ssl); peer_cert = SSL_get_peer_certificate(ssl); if (type & REGRESS_OPENSSL_SERVER) { @@ -391,30 +238,30 @@ eventcb(struct bufferevent *bev, short what, void *ctx) TT_BLATHER(("Got a good EOF")); ++got_close; if (type & REGRESS_OPENSSL_FD) { - bufferevent_openssl_check_fd(bev, type & REGRESS_OPENSSL_FILTER); + bufferevent_ssl_check_fd(bev, type & REGRESS_OPENSSL_FILTER); } if (type & REGRESS_OPENSSL_FREED) { - bufferevent_openssl_check_freed(bev); + bufferevent_ssl_check_freed(bev); } bufferevent_free(bev); } else if (what & BEV_EVENT_ERROR) { TT_BLATHER(("Got an error.")); ++got_error; if (type & REGRESS_OPENSSL_FD) { - bufferevent_openssl_check_fd(bev, type & REGRESS_OPENSSL_FILTER); + bufferevent_ssl_check_fd(bev, type & REGRESS_OPENSSL_FILTER); } if (type & REGRESS_OPENSSL_FREED) { - bufferevent_openssl_check_freed(bev); + bufferevent_ssl_check_freed(bev); } bufferevent_free(bev); } else if (what & BEV_EVENT_TIMEOUT) { TT_BLATHER(("Got timeout.")); ++got_timeout; if (type & REGRESS_OPENSSL_FD) { - bufferevent_openssl_check_fd(bev, type & REGRESS_OPENSSL_FILTER); + bufferevent_ssl_check_fd(bev, type & REGRESS_OPENSSL_FILTER); } if (type & REGRESS_OPENSSL_FREED) { - bufferevent_openssl_check_freed(bev); + bufferevent_ssl_check_freed(bev); } bufferevent_free(bev); } @@ -434,14 +281,14 @@ open_ssl_bufevs(struct bufferevent **bev1_out, struct bufferevent **bev2_out, int state2 = is_open ? BUFFEREVENT_SSL_OPEN :BUFFEREVENT_SSL_ACCEPTING; int dirty_shutdown = type & REGRESS_OPENSSL_DIRTY_SHUTDOWN; if (fd_pair) { - *bev1_out = bufferevent_openssl_socket_new( + *bev1_out = bufferevent_ssl_socket_new( base, fd_pair[0], ssl1, state1, flags); - *bev2_out = bufferevent_openssl_socket_new( + *bev2_out = bufferevent_ssl_socket_new( base, fd_pair[1], ssl2, state2, flags); } else { - *bev1_out = bufferevent_openssl_filter_new( + *bev1_out = bufferevent_ssl_filter_new( base, underlying_pair[0], ssl1, state1, flags); - *bev2_out = bufferevent_openssl_filter_new( + *bev2_out = bufferevent_ssl_filter_new( base, underlying_pair[1], ssl2, state2, flags); } @@ -450,8 +297,8 @@ open_ssl_bufevs(struct bufferevent **bev1_out, struct bufferevent **bev2_out, bufferevent_setcb(*bev2_out, respond_to_number, done_writing_cb, eventcb, (void*)(REGRESS_OPENSSL_SERVER | (long)type)); - bufferevent_openssl_set_allow_dirty_shutdown(*bev1_out, dirty_shutdown); - bufferevent_openssl_set_allow_dirty_shutdown(*bev2_out, dirty_shutdown); + bufferevent_ssl_set_allow_dirty_shutdown(*bev1_out, dirty_shutdown); + bufferevent_ssl_set_allow_dirty_shutdown(*bev2_out, dirty_shutdown); } static void @@ -478,8 +325,8 @@ regress_bufferevent_openssl(void *arg) renegotiate_at = 600; } - ssl1 = SSL_new(get_ssl_ctx()); - ssl2 = SSL_new(get_ssl_ctx()); + ssl1 = SSL_new(get_ssl_ctx(SSL_IS_CLIENT)); + ssl2 = SSL_new(get_ssl_ctx(SSL_IS_SERVER)); SSL_use_certificate(ssl2, the_cert); SSL_use_PrivateKey(ssl2, the_key); @@ -581,14 +428,14 @@ acceptcb(struct evconnlistener *listener, evutil_socket_t fd, struct basic_test_data *data = arg; struct bufferevent *bev; enum regress_openssl_type type; - SSL *ssl = SSL_new(get_ssl_ctx()); + SSL *ssl = SSL_new(get_ssl_ctx(SSL_IS_SERVER)); type = (enum regress_openssl_type)data->setup_data; SSL_use_certificate(ssl, the_cert); SSL_use_PrivateKey(ssl, the_key); - bev = bufferevent_openssl_socket_new( + bev = bufferevent_ssl_socket_new( data->base, fd, ssl, BUFFEREVENT_SSL_ACCEPTING, BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); tt_assert(bev); @@ -618,110 +465,6 @@ struct rwcount size_t read; size_t write; }; -static int -bio_rwcount_new(BIO *b) -{ - BIO_set_init(b, 0); - BIO_set_data(b, NULL); - return 1; -} -static int -bio_rwcount_free(BIO *b) -{ - TT_BLATHER(("bio_rwcount_free: %p", b)); - if (!b) - return 0; - if (BIO_get_shutdown(b)) { - BIO_set_init(b, 0); - BIO_set_data(b, NULL); - } - return 1; -} -static int -bio_rwcount_read(BIO *b, char *out, int outlen) -{ - struct rwcount *rw = BIO_get_data(b); - ev_ssize_t ret = recv(rw->fd, out, outlen, 0); - ++rw->read; - if (ret == -1 && EVUTIL_ERR_RW_RETRIABLE(EVUTIL_SOCKET_ERROR())) { - BIO_set_retry_read(b); - } - return ret; -} -static int -bio_rwcount_write(BIO *b, const char *in, int inlen) -{ - struct rwcount *rw = BIO_get_data(b); - ev_ssize_t ret = send(rw->fd, in, inlen, 0); - ++rw->write; - if (ret == -1 && EVUTIL_ERR_RW_RETRIABLE(EVUTIL_SOCKET_ERROR())) { - BIO_set_retry_write(b); - } - return ret; -} -static long -bio_rwcount_ctrl(BIO *b, int cmd, long num, void *ptr) -{ - struct rwcount *rw = BIO_get_data(b); - long ret = 0; - switch (cmd) { - case BIO_C_GET_FD: - ret = rw->fd; - break; - case BIO_CTRL_GET_CLOSE: - ret = BIO_get_shutdown(b); - break; - case BIO_CTRL_SET_CLOSE: - BIO_set_shutdown(b, (int)num); - break; - case BIO_CTRL_PENDING: - ret = 0; - break; - case BIO_CTRL_WPENDING: - ret = 0; - break; - case BIO_CTRL_DUP: - case BIO_CTRL_FLUSH: - ret = 1; - break; - } - return ret; -} -static int -bio_rwcount_puts(BIO *b, const char *s) -{ - return bio_rwcount_write(b, s, strlen(s)); -} -#define BIO_TYPE_LIBEVENT_RWCOUNT 0xff1 -static BIO_METHOD *methods_rwcount; - -static BIO_METHOD * -BIO_s_rwcount(void) -{ - if (methods_rwcount == NULL) { - methods_rwcount = BIO_meth_new(BIO_TYPE_LIBEVENT_RWCOUNT, "rwcount"); - if (methods_rwcount == NULL) - return NULL; - BIO_meth_set_write(methods_rwcount, bio_rwcount_write); - BIO_meth_set_read(methods_rwcount, bio_rwcount_read); - BIO_meth_set_puts(methods_rwcount, bio_rwcount_puts); - BIO_meth_set_ctrl(methods_rwcount, bio_rwcount_ctrl); - BIO_meth_set_create(methods_rwcount, bio_rwcount_new); - BIO_meth_set_destroy(methods_rwcount, bio_rwcount_free); - } - return methods_rwcount; -} -static BIO * -BIO_new_rwcount(int close_flag) -{ - BIO *result; - if (!(result = BIO_new(BIO_s_rwcount()))) - return NULL; - BIO_set_init(result, 1); - BIO_set_data(result, NULL); - BIO_set_shutdown(result, !!close_flag); - return result; -} static void regress_bufferevent_openssl_connect(void *arg) @@ -755,10 +498,10 @@ regress_bufferevent_openssl_connect(void *arg) tt_assert(listener); tt_assert(evconnlistener_get_fd(listener) >= 0); - ssl = SSL_new(get_ssl_ctx()); + ssl = SSL_new(get_ssl_ctx(SSL_IS_CLIENT)); tt_assert(ssl); - bev = bufferevent_openssl_socket_new( + bev = bufferevent_ssl_socket_new( data->base, -1, ssl, BUFFEREVENT_SSL_CONNECTING, BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); @@ -777,13 +520,8 @@ regress_bufferevent_openssl_connect(void *arg) /* Possible only when we have fd, since be_openssl can and will overwrite * bio otherwise before */ if (type & REGRESS_OPENSSL_SLEEP) { - BIO *bio; - rw.fd = bufferevent_getfd(bev); - bio = BIO_new_rwcount(0); - tt_assert(bio); - BIO_set_data(bio, &rw); - SSL_set_bio(ssl, bio, bio); + BIO_setup(ssl, &rw); } evbuffer_add_printf(bufferevent_get_output(bev), "1\n"); bufferevent_enable(bev, EV_READ|EV_WRITE); @@ -859,12 +597,12 @@ wm_acceptcb(struct evconnlistener *listener, evutil_socket_t fd, struct wm_context *ctx = arg; struct bufferevent *bev; struct event_base *base = evconnlistener_get_base(listener); - SSL *ssl = SSL_new(get_ssl_ctx()); + SSL *ssl = SSL_new(get_ssl_ctx(SSL_IS_SERVER)); SSL_use_certificate(ssl, the_cert); SSL_use_PrivateKey(ssl, the_key); - bev = bufferevent_openssl_socket_new( + bev = bufferevent_ssl_socket_new( base, fd, ssl, BUFFEREVENT_SSL_ACCEPTING, ctx->flags); TT_BLATHER(("wm_transfer-%s(%p): accept", @@ -940,16 +678,16 @@ regress_bufferevent_openssl_wm(void *arg) tt_assert(listener); tt_assert(evconnlistener_get_fd(listener) >= 0); - ssl = SSL_new(get_ssl_ctx()); + ssl = SSL_new(get_ssl_ctx(SSL_IS_CLIENT)); tt_assert(ssl); if (type & REGRESS_OPENSSL_FILTER) { bev = bufferevent_socket_new(data->base, -1, client.flags); tt_assert(bev); - bev = bufferevent_openssl_filter_new( + bev = bufferevent_ssl_filter_new( base, bev, ssl, BUFFEREVENT_SSL_CONNECTING, client.flags); } else { - bev = bufferevent_openssl_socket_new( + bev = bufferevent_ssl_socket_new( data->base, -1, ssl, BUFFEREVENT_SSL_CONNECTING, client.flags); @@ -988,7 +726,7 @@ end: event_base_loop(base, EVLOOP_ONCE); } -struct testcase_t ssl_testcases[] = { +struct testcase_t TESTCASES_NAME[] = { #define T(a) ((void *)(a)) { "bufferevent_socketpair", regress_bufferevent_openssl, TT_ISOLATED, &ssl_setup, T(REGRESS_OPENSSL_SOCKETPAIR) }, @@ -1057,12 +795,10 @@ struct testcase_t ssl_testcases[] = { { "bufferevent_socketpair_timeout_freed_fd", regress_bufferevent_openssl, TT_ISOLATED, &ssl_setup, T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_TIMEOUT | REGRESS_OPENSSL_FREED | REGRESS_OPENSSL_FD) }, - { "bufferevent_connect", regress_bufferevent_openssl_connect, TT_FORK|TT_NEED_BASE, &ssl_setup, NULL }, { "bufferevent_connect_sleep", regress_bufferevent_openssl_connect, TT_FORK|TT_NEED_BASE, &ssl_setup, T(REGRESS_OPENSSL_SLEEP) }, - { "bufferevent_wm", regress_bufferevent_openssl_wm, TT_FORK|TT_NEED_BASE, &ssl_setup, NULL }, { "bufferevent_wm_filter", regress_bufferevent_openssl_wm, From dad699cc044748b0706049aa86df305f63cb0d1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?okhowang=28=E7=8E=8B=E6=B2=9B=E6=96=87=29?= Date: Wed, 27 May 2020 15:29:42 +0800 Subject: [PATCH 5/9] add mbedtls to CI --- .github/workflows/linux.yml | 10 ++++++++++ .github/workflows/macos.yml | 5 ++++- .github/workflows/mingw.yml | 8 ++++---- .github/workflows/windows.yml | 20 +++++++++----------- 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 703fb962..129eb8e9 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -53,6 +53,11 @@ jobs: path: dist key: ${{ matrix.os }}-cmake-dist-${{ matrix.EVENT_MATRIX }}-v2 + - name: Install Depends + run: | + sudo apt-get update + sudo apt-get install -y libmbedtls-dev + - name: Build And Test shell: bash run: | @@ -153,6 +158,11 @@ jobs: path: dist key: ${{ matrix.os }}-autotools-dist-${{ matrix.EVENT_MATRIX }}-v2 + - name: Install Depends + run: | + sudo apt-get update + sudo apt-get install -y libmbedtls-dev + - name: Build And Test shell: bash run: | diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 910a627a..84b4c9ec 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -45,6 +45,9 @@ jobs: path: build key: macos-10.15-cmake-${{ matrix.EVENT_MATRIX }}-v2 + - name: Install Depends + run: brew install mbedtls + - name: Build And Test shell: bash run: | @@ -124,7 +127,7 @@ jobs: key: ${{ matrix.os }}-autotools-${{ matrix.EVENT_MATRIX }}-v2 - name: Install Depends - run: brew install autoconf automake libtool pkg-config + run: brew install autoconf automake libtool pkg-config mbedtls - name: Build And Test shell: bash diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index a297e8ca..d0f2af85 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -46,7 +46,7 @@ jobs: uses: actions/cache@v1.1.2 with: path: build - key: mingw-autotools-${{ matrix.EVENT_MATRIX }}-v2 + key: mingw-autotools-${{ matrix.EVENT_MATRIX }}-v3 - uses: numworks/setup-msys2@v1 if: steps.cache-mingw.outputs.cache-hit != 'true' @@ -56,7 +56,7 @@ jobs: - name: Install Dependes if: steps.cache-mingw.outputs.cache-hit != 'true' run: | - msys2do pacman -S --noconfirm mingw-w64-x86_64-gcc autoconf automake libtool mingw-w64-x86_64-openssl + msys2do pacman -S --noconfirm mingw-w64-x86_64-gcc autoconf automake libtool mingw-w64-x86_64-openssl mingw-w64-x86_64-mbedtls - name: Build And Test shell: powershell @@ -115,7 +115,7 @@ jobs: uses: actions/cache@v1.1.2 with: path: build - key: mingw-cmake-${{ matrix.EVENT_MATRIX }}-v2 + key: mingw-cmake-${{ matrix.EVENT_MATRIX }}-v3 - uses: numworks/setup-msys2@v1 if: steps.cache-mingw-cmake.outputs.cache-hit != 'true' @@ -125,7 +125,7 @@ jobs: - name: Install Dependes if: steps.cache-mingw-cmake.outputs.cache-hit != 'true' run: | - msys2do pacman -S --noconfirm mingw-w64-x86_64-gcc mingw-w64-x86_64-openssl + msys2do pacman -S --noconfirm mingw-w64-x86_64-gcc mingw-w64-x86_64-openssl mingw-w64-x86_64-mbedtls - name: Build And Test shell: powershell diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index b30038a0..82538156 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -36,7 +36,7 @@ jobs: uses: actions/cache@v1.0.3 with: path: C:\vcpkg\installed - key: ${{ matrix.os }}-vcpkg + key: ${{ matrix.os }}-vcpkg-v2 - name: Cache Build uses: actions/cache@v1.0.3 @@ -50,19 +50,18 @@ jobs: run: | vcpkg install openssl:x64-windows vcpkg install zlib:x64-windows + vcpkg install mbedtls:x64-windows - name: Build And Test shell: powershell run: | - $OPENSSL_ROOT_DIR="C:\vcpkg\installed\x64-windows" $EVENT_BUILD_PARALLEL=10 $EVENT_TESTS_PARALLEL=1 - $env:PATH="$OPENSSL_ROOT_DIR/bin;$env:PATH" mkdir build -ea 0 cd build - $CMAKE_CMD="cmake -G 'Visual Studio 15 2017 Win64' .." + $CMAKE_CMD="cmake -G 'Visual Studio 15 2017 Win64' -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake .." function cmake_configure($retry) { $errcode=0 @@ -142,7 +141,7 @@ jobs: uses: actions/cache@v1.1.0 with: path: C:\vcpkg\installed - key: ${{ matrix.os }}-vcpkg + key: ${{ matrix.os }}-vcpkg-v2 - name: Cache Build uses: actions/cache@v1.1.0 @@ -156,17 +155,16 @@ jobs: run: | vcpkg install openssl:x64-windows vcpkg install zlib:x64-windows + vcpkg install mbedtls:x64-windows - name: Build And Test shell: powershell run: | - $OPENSSL_ROOT_DIR="C:\vcpkg\installed\x64-windows" $EVENT_BUILD_PARALLEL=10 $EVENT_TESTS_PARALLEL=1 - $env:PATH="$OPENSSL_ROOT_DIR/bin;$env:PATH" if ( "${{ matrix.EVENT_MATRIX }}" -eq "LIBRARY_TYPE_STATIC" ) { - $EVENT_CMAKE_OPTIONS="-DEVENT__LIBRARY_TYPE=STATIC" + $EVENT_CMAKE_OPTIONS="-DEVENT__LIBRARY_TYPE=STATIC -DEVENT__MSVC_STATIC_RUNTIME=OFF" } elseif ( "${{ matrix.EVENT_MATRIX }}" -eq "DISABLE_OPENSSL" ) { $EVENT_CMAKE_OPTIONS="-DEVENT__DISABLE_OPENSSL=ON" @@ -187,7 +185,7 @@ jobs: $EVENT_CMAKE_OPTIONS="-DEVENT__DISABLE_TESTS=ON -DEVENT__DISABLE_SAMPLES=ON" } elseif ( "${{ matrix.EVENT_MATRIX }}" -eq "TEST_EXPORT_STATIC" ) { - $EVENT_CMAKE_OPTIONS="-DEVENT__LIBRARY_TYPE=STATIC -DEVENT__DISABLE_TESTS=ON -DEVENT__DISABLE_SAMPLES=ON" + $EVENT_CMAKE_OPTIONS="-DEVENT__LIBRARY_TYPE=STATIC -DEVENT__MSVC_STATIC_RUNTIME=OFF -DEVENT__DISABLE_TESTS=ON -DEVENT__DISABLE_SAMPLES=ON" } else { $EVENT_CMAKE_OPTIONS="" @@ -197,10 +195,10 @@ jobs: cd build if ("${{ matrix.os }}" -eq "windows-2016") { - $CMAKE_CMD="cmake -G 'Visual Studio 15 2017 Win64' .." + $CMAKE_CMD="cmake -G 'Visual Studio 15 2017 Win64' -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake .." } else { # windows-2019 - $CMAKE_CMD="cmake -G 'Visual Studio 16 2019' -A x64 .. $EVENT_CMAKE_OPTIONS" + $CMAKE_CMD="cmake -G 'Visual Studio 16 2019' -A x64 -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake .. $EVENT_CMAKE_OPTIONS" } function cmake_configure($retry) { 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: [PATCH 6/9] 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 | 1367 +++++++--------------------------------- bufferevent_openssl.c | 1220 ++++------------------------------- bufferevent_ssl.c | 1093 ++++++++++++++++++++++++++++++++ ssl-compat.h | 102 +++ 7 files changed, 1552 insertions(+), 2248 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 "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 -#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 + +#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 "ssl-compat.h" +#include "mm-internal.h" + +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,864 +218,125 @@ 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) +be_mbedtls_bio_set_fd(struct bufferevent_ssl *bev_ssl, evutil_socket_t fd) { - 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); - } - + struct mbedtls_context *ctx = bev_ssl->ssl; 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); + ctx->net.fd = fd; + mbedtls_ssl_set_bio( + ctx->ssl, &ctx->net, mbedtls_net_send, mbedtls_net_recv, NULL); } 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) -{ - 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; - } 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; - } - } -} - -static void -be_mbedtls_handshakecb(struct bufferevent *bev_base, void *ctx) -{ - struct bufferevent_mbedtls *bev_ssl = ctx; - do_handshake(bev_ssl);/* XXX handle failure */ -} - -static void -be_mbedtls_handshakeeventcb(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, 0); - } else - do_handshake(bev_ssl);/* XXX handle failure */ - bufferevent_decref_and_unlock_(&bev_ssl->bev.bev); -} - -static int -set_handshake_callbacks(struct bufferevent_mbedtls *bev_ssl, evutil_socket_t fd) -{ - 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; + mbedtls_ssl_set_bio(ctx->ssl, bev_ssl->underlying, + bio_bufferevent_write, bio_bufferevent_read, NULL); } + return 0; } int -bufferevent_mbedtls_renegotiate(struct bufferevent *bev) +bufferevent_mbedtls_get_allow_dirty_shutdown(struct bufferevent *bev) { - struct bufferevent_mbedtls *bev_ssl = upcast(bev); - 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; + return bufferevent_ssl_get_allow_dirty_shutdown(bev); } -static void -be_mbedtls_outbuf_cb(struct evbuffer *buf, - const struct evbuffer_cb_info *cbinfo, void *arg) +void +bufferevent_mbedtls_set_allow_dirty_shutdown( + struct bufferevent *bev, int allow_dirty_shutdown) { - 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; + bufferevent_ssl_set_allow_dirty_shutdown(bev, allow_dirty_shutdown); } - -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; -} - -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 * +mbedtls_ssl_context * bufferevent_mbedtls_get_ssl(struct bufferevent *bufev) { - struct bufferevent_mbedtls *bev_ssl = upcast(bufev); + struct mbedtls_context *ctx = NULL; + struct bufferevent_ssl *bev_ssl = bufferevent_ssl_upcast(bufev); if (!bev_ssl) return NULL; - return bev_ssl->ssl; + ctx = bev_ssl->ssl; + return ctx->ssl; } -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) +int +bufferevent_mbedtls_renegotiate(struct bufferevent *bufev) { - 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; - - if (bufferevent_init_common_(bev_p, base, - &bufferevent_ops_mbedtls, tmp_options) < 0) - goto err; - - bev_ssl->underlying = underlying; - bev_ssl->ssl = ssl; - - 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); - } - - 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_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_ssl *bev_ssl = bufferevent_ssl_upcast(bufev); + if (!bev_ssl) + return -1; + return bufferevent_ssl_renegotiate_impl(bufev); } +unsigned long +bufferevent_get_mbedtls_error(struct bufferevent *bufev) +{ + struct bufferevent_ssl *bev_ssl = bufferevent_ssl_upcast(bufev); + if (!bev_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, +}; + struct bufferevent * bufferevent_mbedtls_filter_new(struct event_base *base, - struct bufferevent *underlying, - SSL *ssl, - enum bufferevent_ssl_state state, - int options) + struct bufferevent *underlying, mbedtls_ssl_context *ssl, + enum bufferevent_ssl_state state, int options) { struct bufferevent *bev; if (!underlying) goto err; - bev = bufferevent_mbedtls_new_impl( - base, underlying, -1, ssl, state, options); + bev = bufferevent_ssl_new_impl( + base, underlying, -1, ssl, state, options, &le_mbedtls_ops); + + if (bev) { + be_mbedtls_bio_set_fd(bufferevent_ssl_upcast(bev), -1); + } + return bev; err: @@ -1244,58 +346,53 @@ err: } struct bufferevent * -bufferevent_mbedtls_socket_new(struct event_base *base, - evutil_socket_t fd, - 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 (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; + } + } + + 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); -} - -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]; } - BEV_UNLOCK(bev); - return err; + + bev = bufferevent_ssl_new_impl( + base, NULL, fd, ssl, state, options, &le_mbedtls_ops); + + if (bev) { + be_mbedtls_bio_set_fd(bufferevent_ssl_upcast(bev), fd); + } + + 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,49 +24,18 @@ * 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 #include #include "openssl-compat.h" +#include "event2/bufferevent.h" +#include "event2/bufferevent_struct.h" +#include "event2/buffer.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 +static void * +SSL_init(void *ssl) +{ + return ssl; +} + +static void +SSL_context_free(void *ssl, int flags) +{ + if (flags & BEV_OPT_CLOSE_ON_FREE) + SSL_free(ssl); +} -/* 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; +SSL_is_ok(int err) +{ + return err == 1; } -/* 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) +SSL_is_want_read(int err) { - 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; + return err == SSL_ERROR_WANT_READ; } -#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) +SSL_is_want_write(int err) { - 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) -{ - 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 err == SSL_ERROR_WANT_WRITE; } static void -consider_writing(struct bufferevent_openssl *bev_ssl) +be_openssl_post_init(void *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); - } - } + /* 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 void -be_openssl_readcb(struct bufferevent *bev_base, void *ctx) +static int +openssl_read(void *ssl, unsigned char *buf, size_t len) { - struct bufferevent_openssl *bev_ssl = ctx; - consider_reading(bev_ssl); + return SSL_read(ssl, buf, len); } -static void -be_openssl_writecb(struct bufferevent *bev_base, void *ctx) +static int +openssl_write(void *ssl, const unsigned char *buf, size_t len) { - 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); + return SSL_write(ssl, buf, len); } static evutil_socket_t -be_openssl_auto_fd(struct bufferevent_openssl *bev_ssl, evutil_socket_t fd) +be_openssl_get_fd(struct bufferevent_ssl *bev_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); - } - } + 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 -set_open_callbacks(struct bufferevent_openssl *bev_ssl, evutil_socket_t fd) +be_openssl_bio_set_fd(struct bufferevent_ssl *bev_ssl, evutil_socket_t fd) { - if (bev_ssl->underlying) { - bufferevent_setcb(bev_ssl->underlying, - be_openssl_readcb, be_openssl_writecb, be_openssl_eventcb, - bev_ssl); - return 0; + if (!bev_ssl->underlying) { + BIO *bio; + bio = BIO_new_socket((int)fd, 0); + SSL_set_bio(bev_ssl->ssl, bio, bio); } 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; - } -} - -static int -do_handshake(struct bufferevent_openssl *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: - 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); + BIO *bio; + if (!(bio = BIO_new_bufferevent(bev_ssl->underlying))) 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); -} - -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) -{ - 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; -} - -static void -be_openssl_outbuf_cb(struct evbuffer *buf, - const struct evbuffer_cb_info *cbinfo, void *arg) -{ - 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; -} - - -static int -be_openssl_enable(struct bufferevent *bev, short events) -{ - 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; -} - -static int -be_openssl_disable(struct bufferevent *bev, short events) -{ - 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); + SSL_set_bio(bev_ssl->ssl, bio, bio); } return 0; } -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) -{ - 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); - } -} - -static int -be_openssl_adj_timeouts(struct bufferevent *bev) -{ - struct bufferevent_openssl *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_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) - return -1; - break; - default: - return -1; - } - - 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 */ From e1cdf1a182ca2cd3b8e1b9b78d2cc133cb489476 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Wed, 22 Jul 2020 23:02:31 +0300 Subject: [PATCH 7/9] Update LICENSE for ssl-client-mbedtls.c --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/LICENSE b/LICENSE index f7afe7cc..e86a5551 100644 --- a/LICENSE +++ b/LICENSE @@ -128,3 +128,24 @@ 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. + +============================== + +The ssl-client-mbedtls.c is available under the following license: + +Copyright (C) 2006-2015, ARM Limited, All Rights Reserved +SPDX-License-Identifier: Apache-2.0 + +Licensed under the Apache License, Version 2.0 (the "License"); you may +not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +This file is part of mbed TLS (https://tls.mbed.org) From 5671575a1c2a8dba719a33464673b2a75d3877ea Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Wed, 22 Jul 2020 23:08:50 +0300 Subject: [PATCH 8/9] Join le_ssl_ops.post_init with le_ssl_ops.init --- bufferevent_mbedtls.c | 1 - bufferevent_openssl.c | 13 ++++--------- bufferevent_ssl.c | 1 - ssl-compat.h | 1 - 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/bufferevent_mbedtls.c b/bufferevent_mbedtls.c index f1422019..c60b109c 100644 --- a/bufferevent_mbedtls.c +++ b/bufferevent_mbedtls.c @@ -313,7 +313,6 @@ static struct le_ssl_ops le_mbedtls_ops = { 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, diff --git a/bufferevent_openssl.c b/bufferevent_openssl.c index a88ae891..c50c022d 100644 --- a/bufferevent_openssl.c +++ b/bufferevent_openssl.c @@ -322,6 +322,10 @@ decrement_buckets(struct bufferevent_ssl *bev_ssl) static void * SSL_init(void *ssl) { + /* 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); + return ssl; } @@ -350,14 +354,6 @@ SSL_is_want_write(int err) return err == SSL_ERROR_WANT_WRITE; } -static void -be_openssl_post_init(void *ssl) -{ - /* 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 openssl_read(void *ssl, unsigned char *buf, size_t len) { @@ -415,7 +411,6 @@ static struct le_ssl_ops le_openssl_ops = { 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, diff --git a/bufferevent_ssl.c b/bufferevent_ssl.c index be503819..5c6956d5 100644 --- a/bufferevent_ssl.c +++ b/bufferevent_ssl.c @@ -1007,7 +1007,6 @@ bufferevent_ssl_new_impl(struct event_base *base, 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; diff --git a/ssl-compat.h b/ssl-compat.h index ee88b624..a19c457f 100644 --- a/ssl-compat.h +++ b/ssl-compat.h @@ -25,7 +25,6 @@ struct le_ssl_ops { 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)( From 1bfbbdf2b4361e9e84b82125539c00ae21b0bfdf Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Wed, 22 Jul 2020 23:10:17 +0300 Subject: [PATCH 9/9] test: rename ssl/* -> openssl/* --- test/regress.h | 2 +- test/regress_main.c | 2 +- test/regress_openssl.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/regress.h b/test/regress.h index 35965715..b7c4e0bc 100644 --- a/test/regress.h +++ b/test/regress.h @@ -49,7 +49,7 @@ extern struct testcase_t rpc_testcases[]; extern struct testcase_t edgetriggered_testcases[]; extern struct testcase_t minheap_testcases[]; extern struct testcase_t iocp_testcases[]; -extern struct testcase_t ssl_testcases[]; +extern struct testcase_t openssl_testcases[]; extern struct testcase_t mbedtls_testcases[]; extern struct testcase_t listener_testcases[]; extern struct testcase_t listener_iocp_testcases[]; diff --git a/test/regress_main.c b/test/regress_main.c index 35945938..9f0dcaf2 100644 --- a/test/regress_main.c +++ b/test/regress_main.c @@ -451,7 +451,7 @@ struct testgroup_t testgroups[] = { { "iocp/http/", http_iocp_testcases }, #endif #ifdef EVENT__HAVE_OPENSSL - { "ssl/", ssl_testcases }, + { "openssl/", openssl_testcases }, #endif #ifdef EVENT__HAVE_MBEDTLS { "mbedtls/", mbedtls_testcases }, diff --git a/test/regress_openssl.c b/test/regress_openssl.c index 1a0ddd0e..1bf0b50d 100644 --- a/test/regress_openssl.c +++ b/test/regress_openssl.c @@ -29,7 +29,7 @@ #include "openssl-compat.h" #include "regress.h" #include "tinytest.h" -#define TESTCASES_NAME ssl_testcases +#define TESTCASES_NAME openssl_testcases static void *ssl_test_setup(const struct testcase_t *testcase); static int ssl_test_cleanup(const struct testcase_t *testcase, void *ptr); static const struct testcase_setup_t ssl_setup = {