mirror of
https://github.com/libevent/libevent.git
synced 2025-01-20 05:02:55 +08:00
312 lines
7.6 KiB
C
312 lines
7.6 KiB
C
/*
|
|
* 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 <openssl/err.h>
|
|
#include <openssl/pem.h>
|
|
#include "openssl-compat.h"
|
|
#include "regress.h"
|
|
#include "tinytest.h"
|
|
#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 = {
|
|
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;
|
|
}
|