michael@0: /* michael@0: * Copyright (c) 2009-2012 Niels Provos and Nick Mathewson michael@0: * michael@0: * Redistribution and use in source and binary forms, with or without michael@0: * modification, are permitted provided that the following conditions michael@0: * are met: michael@0: * 1. Redistributions of source code must retain the above copyright michael@0: * notice, this list of conditions and the following disclaimer. michael@0: * 2. Redistributions in binary form must reproduce the above copyright michael@0: * notice, this list of conditions and the following disclaimer in the michael@0: * documentation and/or other materials provided with the distribution. michael@0: * 3. The name of the author may not be used to endorse or promote products michael@0: * derived from this software without specific prior written permission. michael@0: * michael@0: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR michael@0: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES michael@0: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. michael@0: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, michael@0: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT michael@0: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF michael@0: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: */ michael@0: michael@0: #ifdef WIN32 michael@0: #include michael@0: #include michael@0: #endif michael@0: michael@0: #ifndef WIN32 michael@0: #include michael@0: #include michael@0: #include michael@0: #endif michael@0: michael@0: #include "event2/util.h" michael@0: #include "event2/event.h" michael@0: #include "event2/bufferevent_ssl.h" michael@0: #include "event2/buffer.h" michael@0: #include "event2/listener.h" michael@0: michael@0: #include "regress.h" michael@0: #include "tinytest.h" michael@0: #include "tinytest_macros.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include michael@0: michael@0: /* A short pre-generated key, to save the cost of doing an RSA key generation michael@0: * step during the unit tests. It's only 512 bits long, and it is published michael@0: * in this file, so you would have to be very foolish to consider using it in michael@0: * your own code. */ michael@0: static const char KEY[] = michael@0: "-----BEGIN RSA PRIVATE KEY-----\n" michael@0: "MIIBOgIBAAJBAKibTEzXjj+sqpipePX1lEk5BNFuL/dDBbw8QCXgaJWikOiKHeJq\n" michael@0: "3FQ0OmCnmpkdsPFE4x3ojYmmdgE2i0dJwq0CAwEAAQJAZ08gpUS+qE1IClps/2gG\n" michael@0: "AAer6Bc31K2AaiIQvCSQcH440cp062QtWMC3V5sEoWmdLsbAHFH26/9ZHn5zAflp\n" michael@0: "gQIhANWOx/UYeR8HD0WREU5kcuSzgzNLwUErHLzxP7U6aojpAiEAyh2H35CjN/P7\n" michael@0: "NhcZ4QYw3PeUWpqgJnaE/4i80BSYkSUCIQDLHFhLYLJZ80HwHTADif/ISn9/Ow6b\n" michael@0: "p6BWh3DbMar/eQIgBPS6azH5vpp983KXkNv9AL4VZi9ac/b+BeINdzC6GP0CIDmB\n" michael@0: "U6GFEQTZ3IfuiVabG5pummdC4DNbcdI+WKrSFNmQ\n" michael@0: "-----END RSA PRIVATE KEY-----\n"; michael@0: michael@0: static EVP_PKEY * michael@0: getkey(void) michael@0: { michael@0: EVP_PKEY *key; michael@0: BIO *bio; michael@0: michael@0: /* new read-only BIO backed by KEY. */ michael@0: bio = BIO_new_mem_buf((char*)KEY, -1); michael@0: tt_assert(bio); michael@0: michael@0: key = PEM_read_bio_PrivateKey(bio,NULL,NULL,NULL); michael@0: BIO_free(bio); michael@0: tt_assert(key); michael@0: michael@0: return key; michael@0: end: michael@0: return NULL; michael@0: } michael@0: michael@0: static X509 * michael@0: getcert(void) michael@0: { michael@0: /* Dummy code to make a quick-and-dirty valid certificate with michael@0: OpenSSL. Don't copy this code into your own program! It does a michael@0: number of things in a stupid and insecure way. */ michael@0: X509 *x509 = NULL; michael@0: X509_NAME *name = NULL; michael@0: EVP_PKEY *key = getkey(); michael@0: int nid; michael@0: time_t now = time(NULL); michael@0: michael@0: tt_assert(key); michael@0: michael@0: x509 = X509_new(); michael@0: tt_assert(x509); michael@0: tt_assert(0 != X509_set_version(x509, 2)); michael@0: tt_assert(0 != ASN1_INTEGER_set(X509_get_serialNumber(x509), michael@0: (long)now)); michael@0: michael@0: name = X509_NAME_new(); michael@0: tt_assert(name); michael@0: nid = OBJ_txt2nid("commonName"); michael@0: tt_assert(NID_undef != nid); michael@0: tt_assert(0 != X509_NAME_add_entry_by_NID( michael@0: name, nid, MBSTRING_ASC, (unsigned char*)"example.com", michael@0: -1, -1, 0)); michael@0: michael@0: X509_set_subject_name(x509, name); michael@0: X509_set_issuer_name(x509, name); michael@0: michael@0: X509_time_adj(X509_get_notBefore(x509), 0, &now); michael@0: now += 3600; michael@0: X509_time_adj(X509_get_notAfter(x509), 0, &now); michael@0: X509_set_pubkey(x509, key); michael@0: tt_assert(0 != X509_sign(x509, key, EVP_sha1())); michael@0: michael@0: return x509; michael@0: end: michael@0: X509_free(x509); michael@0: return NULL; michael@0: } michael@0: michael@0: static int disable_tls_11_and_12 = 0; michael@0: static SSL_CTX *the_ssl_ctx = NULL; michael@0: michael@0: static SSL_CTX * michael@0: get_ssl_ctx(void) michael@0: { michael@0: if (the_ssl_ctx) michael@0: return the_ssl_ctx; michael@0: the_ssl_ctx = SSL_CTX_new(SSLv23_method()); michael@0: if (!the_ssl_ctx) michael@0: return NULL; michael@0: if (disable_tls_11_and_12) { michael@0: #ifdef SSL_OP_NO_TLSv1_2 michael@0: SSL_CTX_set_options(the_ssl_ctx, SSL_OP_NO_TLSv1_2); michael@0: #endif michael@0: #ifdef SSL_OP_NO_TLSv1_1 michael@0: SSL_CTX_set_options(the_ssl_ctx, SSL_OP_NO_TLSv1_1); michael@0: #endif michael@0: } michael@0: return the_ssl_ctx; michael@0: } michael@0: michael@0: static void michael@0: init_ssl(void) michael@0: { michael@0: SSL_library_init(); michael@0: ERR_load_crypto_strings(); michael@0: SSL_load_error_strings(); michael@0: OpenSSL_add_all_algorithms(); michael@0: if (SSLeay() != OPENSSL_VERSION_NUMBER) { michael@0: TT_DECLARE("WARN", ("Version mismatch for openssl: compiled with %lx but running with %lx", (unsigned long)OPENSSL_VERSION_NUMBER, (unsigned long)SSLeay())); michael@0: } michael@0: } michael@0: michael@0: /* ==================== michael@0: Here's a simple test: we read a number from the input, increment it, and michael@0: reply, until we get to 1001. michael@0: */ michael@0: michael@0: static int test_is_done = 0; michael@0: static int n_connected = 0; michael@0: static int got_close = 0; michael@0: static int got_error = 0; michael@0: static int renegotiate_at = -1; michael@0: static int stop_when_connected = 0; michael@0: static int pending_connect_events = 0; michael@0: static struct event_base *exit_base = NULL; michael@0: michael@0: static void michael@0: respond_to_number(struct bufferevent *bev, void *ctx) michael@0: { michael@0: struct evbuffer *b = bufferevent_get_input(bev); michael@0: char *line; michael@0: int n; michael@0: line = evbuffer_readln(b, NULL, EVBUFFER_EOL_LF); michael@0: if (! line) michael@0: return; michael@0: n = atoi(line); michael@0: if (n <= 0) michael@0: TT_FAIL(("Bad number: %s", line)); michael@0: TT_BLATHER(("The number was %d", n)); michael@0: if (n == 1001) { michael@0: ++test_is_done; michael@0: bufferevent_free(bev); /* Should trigger close on other side. */ michael@0: return; michael@0: } michael@0: if (!strcmp(ctx, "client") && n == renegotiate_at) { michael@0: SSL_renegotiate(bufferevent_openssl_get_ssl(bev)); michael@0: } michael@0: ++n; michael@0: evbuffer_add_printf(bufferevent_get_output(bev), michael@0: "%d\n", n); michael@0: TT_BLATHER(("Done reading; now writing.")); michael@0: bufferevent_enable(bev, EV_WRITE); michael@0: bufferevent_disable(bev, EV_READ); michael@0: } michael@0: michael@0: static void michael@0: done_writing_cb(struct bufferevent *bev, void *ctx) michael@0: { michael@0: struct evbuffer *b = bufferevent_get_output(bev); michael@0: if (evbuffer_get_length(b)) michael@0: return; michael@0: TT_BLATHER(("Done writing.")); michael@0: bufferevent_disable(bev, EV_WRITE); michael@0: bufferevent_enable(bev, EV_READ); michael@0: } michael@0: michael@0: static void michael@0: eventcb(struct bufferevent *bev, short what, void *ctx) michael@0: { michael@0: TT_BLATHER(("Got event %d", (int)what)); michael@0: if (what & BEV_EVENT_CONNECTED) { michael@0: SSL *ssl; michael@0: X509 *peer_cert; michael@0: ++n_connected; michael@0: ssl = bufferevent_openssl_get_ssl(bev); michael@0: tt_assert(ssl); michael@0: peer_cert = SSL_get_peer_certificate(ssl); michael@0: if (0==strcmp(ctx, "server")) { michael@0: tt_assert(peer_cert == NULL); michael@0: } else { michael@0: tt_assert(peer_cert != NULL); michael@0: } michael@0: if (stop_when_connected) { michael@0: if (--pending_connect_events == 0) michael@0: event_base_loopexit(exit_base, NULL); michael@0: } michael@0: } else if (what & BEV_EVENT_EOF) { michael@0: TT_BLATHER(("Got a good EOF")); michael@0: ++got_close; michael@0: bufferevent_free(bev); michael@0: } else if (what & BEV_EVENT_ERROR) { michael@0: TT_BLATHER(("Got an error.")); michael@0: ++got_error; michael@0: bufferevent_free(bev); michael@0: } michael@0: end: michael@0: ; michael@0: } michael@0: michael@0: static void michael@0: open_ssl_bufevs(struct bufferevent **bev1_out, struct bufferevent **bev2_out, michael@0: struct event_base *base, int is_open, int flags, SSL *ssl1, SSL *ssl2, michael@0: evutil_socket_t *fd_pair, struct bufferevent **underlying_pair) michael@0: { michael@0: int state1 = is_open ? BUFFEREVENT_SSL_OPEN :BUFFEREVENT_SSL_CONNECTING; michael@0: int state2 = is_open ? BUFFEREVENT_SSL_OPEN :BUFFEREVENT_SSL_ACCEPTING; michael@0: if (fd_pair) { michael@0: *bev1_out = bufferevent_openssl_socket_new( michael@0: base, fd_pair[0], ssl1, state1, flags); michael@0: *bev2_out = bufferevent_openssl_socket_new( michael@0: base, fd_pair[1], ssl2, state2, flags); michael@0: } else { michael@0: *bev1_out = bufferevent_openssl_filter_new( michael@0: base, underlying_pair[0], ssl1, state1, flags); michael@0: *bev2_out = bufferevent_openssl_filter_new( michael@0: base, underlying_pair[1], ssl2, state2, flags); michael@0: michael@0: } michael@0: bufferevent_setcb(*bev1_out, respond_to_number, done_writing_cb, michael@0: eventcb, (void*)"client"); michael@0: bufferevent_setcb(*bev2_out, respond_to_number, done_writing_cb, michael@0: eventcb, (void*)"server"); michael@0: } michael@0: michael@0: static void michael@0: regress_bufferevent_openssl(void *arg) michael@0: { michael@0: struct basic_test_data *data = arg; michael@0: michael@0: struct bufferevent *bev1, *bev2; michael@0: SSL *ssl1, *ssl2; michael@0: X509 *cert = getcert(); michael@0: EVP_PKEY *key = getkey(); michael@0: const int start_open = strstr((char*)data->setup_data, "open")!=NULL; michael@0: const int filter = strstr((char*)data->setup_data, "filter")!=NULL; michael@0: int flags = BEV_OPT_DEFER_CALLBACKS; michael@0: struct bufferevent *bev_ll[2] = { NULL, NULL }; michael@0: evutil_socket_t *fd_pair = NULL; michael@0: michael@0: tt_assert(cert); michael@0: tt_assert(key); michael@0: michael@0: init_ssl(); michael@0: michael@0: if (strstr((char*)data->setup_data, "renegotiate")) { michael@0: if (SSLeay() >= 0x10001000 && michael@0: SSLeay() < 0x1000104f) { michael@0: /* 1.0.1 up to 1.0.1c has a bug where TLS1.1 and 1.2 michael@0: * can't renegotiate with themselves. Disable. */ michael@0: disable_tls_11_and_12 = 1; michael@0: } michael@0: renegotiate_at = 600; michael@0: } michael@0: michael@0: ssl1 = SSL_new(get_ssl_ctx()); michael@0: ssl2 = SSL_new(get_ssl_ctx()); michael@0: michael@0: SSL_use_certificate(ssl2, cert); michael@0: SSL_use_PrivateKey(ssl2, key); michael@0: michael@0: if (! start_open) michael@0: flags |= BEV_OPT_CLOSE_ON_FREE; michael@0: michael@0: if (!filter) { michael@0: tt_assert(strstr((char*)data->setup_data, "socketpair")); michael@0: fd_pair = data->pair; michael@0: } else { michael@0: bev_ll[0] = bufferevent_socket_new(data->base, data->pair[0], michael@0: BEV_OPT_CLOSE_ON_FREE); michael@0: bev_ll[1] = bufferevent_socket_new(data->base, data->pair[1], michael@0: BEV_OPT_CLOSE_ON_FREE); michael@0: } michael@0: michael@0: open_ssl_bufevs(&bev1, &bev2, data->base, 0, flags, ssl1, ssl2, michael@0: fd_pair, bev_ll); michael@0: michael@0: if (!filter) { michael@0: tt_int_op(bufferevent_getfd(bev1), ==, data->pair[0]); michael@0: } else { michael@0: tt_ptr_op(bufferevent_get_underlying(bev1), ==, bev_ll[0]); michael@0: } michael@0: michael@0: if (start_open) { michael@0: pending_connect_events = 2; michael@0: stop_when_connected = 1; michael@0: exit_base = data->base; michael@0: event_base_dispatch(data->base); michael@0: /* Okay, now the renegotiation is done. Make new michael@0: * bufferevents to test opening in BUFFEREVENT_SSL_OPEN */ michael@0: flags |= BEV_OPT_CLOSE_ON_FREE; michael@0: bufferevent_free(bev1); michael@0: bufferevent_free(bev2); michael@0: bev1 = bev2 = NULL; michael@0: open_ssl_bufevs(&bev1, &bev2, data->base, 1, flags, ssl1, ssl2, michael@0: fd_pair, bev_ll); michael@0: } michael@0: michael@0: bufferevent_enable(bev1, EV_READ|EV_WRITE); michael@0: bufferevent_enable(bev2, EV_READ|EV_WRITE); michael@0: michael@0: evbuffer_add_printf(bufferevent_get_output(bev1), "1\n"); michael@0: michael@0: event_base_dispatch(data->base); michael@0: michael@0: tt_assert(test_is_done == 1); michael@0: tt_assert(n_connected == 2); michael@0: michael@0: /* We don't handle shutdown properly yet. michael@0: tt_int_op(got_close, ==, 1); michael@0: tt_int_op(got_error, ==, 0); michael@0: */ michael@0: end: michael@0: return; michael@0: } michael@0: michael@0: static void michael@0: acceptcb(struct evconnlistener *listener, evutil_socket_t fd, michael@0: struct sockaddr *addr, int socklen, void *arg) michael@0: { michael@0: struct basic_test_data *data = arg; michael@0: struct bufferevent *bev; michael@0: SSL *ssl = SSL_new(get_ssl_ctx()); michael@0: michael@0: SSL_use_certificate(ssl, getcert()); michael@0: SSL_use_PrivateKey(ssl, getkey()); michael@0: michael@0: bev = bufferevent_openssl_socket_new( michael@0: data->base, michael@0: fd, michael@0: ssl, michael@0: BUFFEREVENT_SSL_ACCEPTING, michael@0: BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); michael@0: michael@0: bufferevent_setcb(bev, respond_to_number, NULL, eventcb, michael@0: (void*)"server"); michael@0: michael@0: bufferevent_enable(bev, EV_READ|EV_WRITE); michael@0: michael@0: /* Only accept once, then disable ourself. */ michael@0: evconnlistener_disable(listener); michael@0: } michael@0: michael@0: static void michael@0: regress_bufferevent_openssl_connect(void *arg) michael@0: { michael@0: struct basic_test_data *data = arg; michael@0: michael@0: struct event_base *base = data->base; michael@0: michael@0: struct evconnlistener *listener; michael@0: struct bufferevent *bev; michael@0: struct sockaddr_in sin; michael@0: struct sockaddr_storage ss; michael@0: ev_socklen_t slen; michael@0: michael@0: init_ssl(); michael@0: michael@0: memset(&sin, 0, sizeof(sin)); michael@0: sin.sin_family = AF_INET; michael@0: sin.sin_addr.s_addr = htonl(0x7f000001); michael@0: michael@0: memset(&ss, 0, sizeof(ss)); michael@0: slen = sizeof(ss); michael@0: michael@0: listener = evconnlistener_new_bind(base, acceptcb, data, michael@0: LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, michael@0: -1, (struct sockaddr *)&sin, sizeof(sin)); michael@0: michael@0: tt_assert(listener); michael@0: tt_assert(evconnlistener_get_fd(listener) >= 0); michael@0: michael@0: bev = bufferevent_openssl_socket_new( michael@0: data->base, -1, SSL_new(get_ssl_ctx()), michael@0: BUFFEREVENT_SSL_CONNECTING, michael@0: BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); michael@0: tt_assert(bev); michael@0: michael@0: bufferevent_setcb(bev, respond_to_number, NULL, eventcb, michael@0: (void*)"client"); michael@0: michael@0: tt_assert(getsockname(evconnlistener_get_fd(listener), michael@0: (struct sockaddr*)&ss, &slen) == 0); michael@0: tt_assert(slen == sizeof(struct sockaddr_in)); michael@0: tt_int_op(((struct sockaddr*)&ss)->sa_family, ==, AF_INET); michael@0: tt_int_op(((struct sockaddr*)&ss)->sa_family, ==, AF_INET); michael@0: michael@0: tt_assert(0 == michael@0: bufferevent_socket_connect(bev, (struct sockaddr*)&ss, slen)); michael@0: evbuffer_add_printf(bufferevent_get_output(bev), "1\n"); michael@0: bufferevent_enable(bev, EV_READ|EV_WRITE); michael@0: michael@0: event_base_dispatch(base); michael@0: end: michael@0: ; michael@0: } michael@0: michael@0: struct testcase_t ssl_testcases[] = { michael@0: michael@0: { "bufferevent_socketpair", regress_bufferevent_openssl, TT_ISOLATED, michael@0: &basic_setup, (void*)"socketpair" }, michael@0: { "bufferevent_filter", regress_bufferevent_openssl, michael@0: TT_ISOLATED, michael@0: &basic_setup, (void*)"filter" }, michael@0: { "bufferevent_renegotiate_socketpair", regress_bufferevent_openssl, michael@0: TT_ISOLATED, michael@0: &basic_setup, (void*)"socketpair renegotiate" }, michael@0: { "bufferevent_renegotiate_filter", regress_bufferevent_openssl, michael@0: TT_ISOLATED, michael@0: &basic_setup, (void*)"filter renegotiate" }, michael@0: { "bufferevent_socketpair_startopen", regress_bufferevent_openssl, michael@0: TT_ISOLATED, &basic_setup, (void*)"socketpair open" }, michael@0: { "bufferevent_filter_startopen", regress_bufferevent_openssl, michael@0: TT_ISOLATED, &basic_setup, (void*)"filter open" }, michael@0: michael@0: { "bufferevent_connect", regress_bufferevent_openssl_connect, michael@0: TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, michael@0: michael@0: END_OF_TESTCASES, michael@0: };