Skip to content

Commit

Permalink
Add a test for a retry during the handshake
Browse files Browse the repository at this point in the history
Test various scenarios for a write retry occuring during a handshake.

Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from #21435)

(cherry picked from commit d6179e6)
  • Loading branch information
mattcaswell authored and paulidale committed Jul 16, 2023
1 parent e1ec729 commit 51af5f2
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 32 deletions.
14 changes: 10 additions & 4 deletions test/helpers/ssltestlib.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ static int tls_dump_puts(BIO *bp, const char *str);
static BIO_METHOD *method_tls_dump = NULL;
static BIO_METHOD *meth_mem = NULL;
static BIO_METHOD *meth_always_retry = NULL;
static int retry_err = -1;

/* Note: Not thread safe! */
const BIO_METHOD *bio_f_tls_dump_filter(void)
Expand Down Expand Up @@ -760,16 +761,21 @@ static int always_retry_free(BIO *bio)
return 1;
}

void set_always_retry_err_val(int err)
{
retry_err = err;
}

static int always_retry_read(BIO *bio, char *out, int outl)
{
BIO_set_retry_read(bio);
return -1;
return retry_err;
}

static int always_retry_write(BIO *bio, const char *in, int inl)
{
BIO_set_retry_write(bio);
return -1;
return retry_err;
}

static long always_retry_ctrl(BIO *bio, int cmd, long num, void *ptr)
Expand All @@ -795,13 +801,13 @@ static long always_retry_ctrl(BIO *bio, int cmd, long num, void *ptr)
static int always_retry_gets(BIO *bio, char *buf, int size)
{
BIO_set_retry_read(bio);
return -1;
return retry_err;
}

static int always_retry_puts(BIO *bio, const char *str)
{
BIO_set_retry_write(bio);
return -1;
return retry_err;
}

int create_ssl_ctx_pair(OSSL_LIB_CTX *libctx, const SSL_METHOD *sm,
Expand Down
1 change: 1 addition & 0 deletions test/helpers/ssltestlib.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ void bio_s_mempacket_test_free(void);

const BIO_METHOD *bio_s_always_retry(void);
void bio_s_always_retry_free(void);
void set_always_retry_err_val(int err);

/* Packet types - value 0 is reserved */
#define INJECT_PACKET 1
Expand Down
165 changes: 137 additions & 28 deletions test/sslapitest.c
Original file line number Diff line number Diff line change
Expand Up @@ -948,18 +948,13 @@ static int test_ccs_change_cipher(void)
}
#endif

static int execute_test_large_message(const SSL_METHOD *smeth,
const SSL_METHOD *cmeth,
int min_version, int max_version,
int read_ahead)
static int add_large_cert_chain(SSL_CTX *sctx)
{
SSL_CTX *cctx = NULL, *sctx = NULL;
SSL *clientssl = NULL, *serverssl = NULL;
int testresult = 0;
int i;
BIO *certbio = NULL;
X509 *chaincert = NULL;
int certlen;
int ret = 0;
int i;

if (!TEST_ptr(certbio = BIO_new_file(cert, "r")))
goto end;
Expand All @@ -972,6 +967,41 @@ static int execute_test_large_message(const SSL_METHOD *smeth,
BIO_free(certbio);
certbio = NULL;

/*
* We assume the supplied certificate is big enough so that if we add
* NUM_EXTRA_CERTS it will make the overall message large enough. The
* default buffer size is requested to be 16k, but due to the way BUF_MEM
* works, it ends up allocating a little over 21k (16 * 4/3). So, in this
* test we need to have a message larger than that.
*/
certlen = i2d_X509(chaincert, NULL);
OPENSSL_assert(certlen * NUM_EXTRA_CERTS >
(SSL3_RT_MAX_PLAIN_LENGTH * 4) / 3);
for (i = 0; i < NUM_EXTRA_CERTS; i++) {
if (!X509_up_ref(chaincert))
goto end;
if (!SSL_CTX_add_extra_chain_cert(sctx, chaincert)) {
X509_free(chaincert);
goto end;
}
}

ret = 1;
end:
BIO_free(certbio);
X509_free(chaincert);
return ret;
}

static int execute_test_large_message(const SSL_METHOD *smeth,
const SSL_METHOD *cmeth,
int min_version, int max_version,
int read_ahead)
{
SSL_CTX *cctx = NULL, *sctx = NULL;
SSL *clientssl = NULL, *serverssl = NULL;
int testresult = 0;

if (!TEST_true(create_ssl_ctx_pair(libctx, smeth, cmeth, min_version,
max_version, &sctx, &cctx, cert,
privkey)))
Expand All @@ -998,24 +1028,8 @@ static int execute_test_large_message(const SSL_METHOD *smeth,
SSL_CTX_set_read_ahead(cctx, 1);
}

/*
* We assume the supplied certificate is big enough so that if we add
* NUM_EXTRA_CERTS it will make the overall message large enough. The
* default buffer size is requested to be 16k, but due to the way BUF_MEM
* works, it ends up allocating a little over 21k (16 * 4/3). So, in this
* test we need to have a message larger than that.
*/
certlen = i2d_X509(chaincert, NULL);
OPENSSL_assert(certlen * NUM_EXTRA_CERTS >
(SSL3_RT_MAX_PLAIN_LENGTH * 4) / 3);
for (i = 0; i < NUM_EXTRA_CERTS; i++) {
if (!X509_up_ref(chaincert))
goto end;
if (!SSL_CTX_add_extra_chain_cert(sctx, chaincert)) {
X509_free(chaincert);
goto end;
}
}
if (!add_large_cert_chain(sctx))
goto end;

if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl,
NULL, NULL))
Expand All @@ -1032,8 +1046,6 @@ static int execute_test_large_message(const SSL_METHOD *smeth,

testresult = 1;
end:
BIO_free(certbio);
X509_free(chaincert);
SSL_free(serverssl);
SSL_free(clientssl);
SSL_CTX_free(sctx);
Expand Down Expand Up @@ -10303,6 +10315,102 @@ static int test_pipelining(int idx)
}
#endif /* !defined(OPENSSL_NO_TLS1_2) && !defined(OPENSSL_NO_DYNAMIC_ENGINE) */

/*
* Force a write retry during handshaking. We test various combinations of
* scenarios. We test a large certificate message which will fill the buffering
* BIO used in the handshake. We try with client auth on and off. Finally we
* also try a BIO that indicates retry via a 0 return. BIO_write() is documented
* to indicate retry via -1 - but sometimes BIOs don't do that.
*
* Test 0: Standard certificate message
* Test 1: Large certificate message
* Test 2: Standard cert, verify peer
* Test 3: Large cert, verify peer
* Test 4: Standard cert, BIO returns 0 on retry
* Test 5: Large cert, BIO returns 0 on retry
* Test 6: Standard cert, verify peer, BIO returns 0 on retry
* Test 7: Large cert, verify peer, BIO returns 0 on retry
* Test 8-15: Repeat of above with TLSv1.2
*/
static int test_handshake_retry(int idx)
{
SSL_CTX *cctx = NULL, *sctx = NULL;
SSL *clientssl = NULL, *serverssl = NULL;
int testresult = 0;
BIO *tmp = NULL, *bretry = BIO_new(bio_s_always_retry());
int maxversion = 0;

if (!TEST_ptr(bretry))
goto end;

#ifndef OPENSSL_NO_TLS1_2
if ((idx & 8) == 8)
maxversion = TLS1_2_VERSION;
#else
if ((idx & 8) == 8)
return TEST_skip("No TLSv1.2");
#endif

if (!TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(),
TLS_client_method(), 0, maxversion,
&sctx, &cctx, cert, privkey)))
goto end;

/*
* Add a large amount of data to fill the buffering BIO used by the SSL
* object
*/
if ((idx & 1) == 1 && !add_large_cert_chain(sctx))
goto end;

/*
* We don't actually configure a client cert, but neither do we fail if one
* isn't present.
*/
if ((idx & 2) == 2)
SSL_CTX_set_verify(sctx, SSL_VERIFY_PEER, NULL);

if ((idx & 4) == 4)
set_always_retry_err_val(0);

if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl,
&clientssl, NULL, NULL)))
goto end;

tmp = SSL_get_wbio(serverssl);
if (!TEST_ptr(tmp) || !TEST_true(BIO_up_ref(tmp))) {
tmp = NULL;
goto end;
}
SSL_set0_wbio(serverssl, bretry);
bretry = NULL;

if (!TEST_int_eq(SSL_connect(clientssl), -1))
goto end;

if (!TEST_int_eq(SSL_accept(serverssl), -1)
|| !TEST_int_eq(SSL_get_error(serverssl, -1), SSL_ERROR_WANT_WRITE))
goto end;

/* Restore a BIO that will let the write succeed */
SSL_set0_wbio(serverssl, tmp);
tmp = NULL;

if (!TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)))
goto end;

testresult = 1;
end:
SSL_free(serverssl);
SSL_free(clientssl);
SSL_CTX_free(sctx);
SSL_CTX_free(cctx);
BIO_free(bretry);
BIO_free(tmp);
set_always_retry_err_val(-1);
return testresult;
}

OPT_TEST_DECLARE_USAGE("certfile privkeyfile srpvfile tmpfile provider config dhfile\n")

int setup_tests(void)
Expand Down Expand Up @@ -10574,6 +10682,7 @@ int setup_tests(void)
#if !defined(OPENSSL_NO_TLS1_2) && !defined(OPENSSL_NO_DYNAMIC_ENGINE)
ADD_ALL_TESTS(test_pipelining, 6);
#endif
ADD_ALL_TESTS(test_handshake_retry, 16);
return 1;

err:
Expand Down

0 comments on commit 51af5f2

Please sign in to comment.