diff options
author | Matt Caswell <matt@openssl.org> | 2023-07-12 14:54:46 +0100 |
---|---|---|
committer | Pauli <pauli@openssl.org> | 2023-07-17 09:43:19 +1000 |
commit | 51af5f2eac820a99c68c831556993fcbca4b4768 (patch) | |
tree | cfe74e8cdb0564f28beb781e39f94db03ae30a5c /test | |
parent | e1ec729a178a1eab38b6413552a6dc9911cd4090 (diff) |
Add a test for a retry during the handshake
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 https://github.com/openssl/openssl/pull/21435)
(cherry picked from commit d6179e6d35af663ca41de615f35a1200a35f85e8)
Diffstat (limited to 'test')
-rw-r--r-- | test/helpers/ssltestlib.c | 14 | ||||
-rw-r--r-- | test/helpers/ssltestlib.h | 1 | ||||
-rw-r--r-- | test/sslapitest.c | 165 |
3 files changed, 148 insertions, 32 deletions
diff --git a/test/helpers/ssltestlib.c b/test/helpers/ssltestlib.c index 02e9c27e5f..ef4a6177aa 100644 --- a/test/helpers/ssltestlib.c +++ b/test/helpers/ssltestlib.c @@ -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) @@ -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) @@ -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, diff --git a/test/helpers/ssltestlib.h b/test/helpers/ssltestlib.h index 50ae279950..8e9daa5601 100644 --- a/test/helpers/ssltestlib.h +++ b/test/helpers/ssltestlib.h @@ -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 diff --git a/test/sslapitest.c b/test/sslapitest.c index 3d571011bd..41eb44419d 100644 --- a/test/sslapitest.c +++ b/test/sslapitest.c @@ -948,18 +948,13 @@ end: } #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; @@ -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))) @@ -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)) @@ -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); @@ -10303,6 +10315,102 @@ end: } #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) @@ -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: |