From 1bee5281e374af3bd7287ce9b2be1044ad888fb2 Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Tue, 5 Mar 2024 15:35:51 +0000 Subject: Extend the multi_resume test for simultaneous resumptions Test what happens if the same session gets resumed multiple times at the same time - and one of them gets marked as not_resumable. Related to CVE-2024-2511 Reviewed-by: Neil Horman Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/24042) (cherry picked from commit cfeaf33a26c53c526128df96db2d2ec105b43aec) --- test/sslapitest.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 4 deletions(-) diff --git a/test/sslapitest.c b/test/sslapitest.c index 6f88b08008..3faba535c0 100644 --- a/test/sslapitest.c +++ b/test/sslapitest.c @@ -11514,12 +11514,62 @@ end: return testresult; } +struct resume_servername_cb_data { + int i; + SSL_CTX *cctx; + SSL_CTX *sctx; + SSL_SESSION *sess; + int recurse; +}; + +/* + * Servername callback. We use it here to run another complete handshake using + * the same session - and mark the session as not_resuamble at the end + */ +static int resume_servername_cb(SSL *s, int *ad, void *arg) +{ + struct resume_servername_cb_data *cbdata = arg; + SSL *serverssl = NULL, *clientssl = NULL; + int ret = SSL_TLSEXT_ERR_ALERT_FATAL; + + if (cbdata->recurse) + return SSL_TLSEXT_ERR_ALERT_FATAL; + + if ((cbdata->i % 3) != 1) + return SSL_TLSEXT_ERR_OK; + + cbdata->recurse = 1; + + if (!TEST_true(create_ssl_objects(cbdata->sctx, cbdata->cctx, &serverssl, + &clientssl, NULL, NULL)) + || !TEST_true(SSL_set_session(clientssl, cbdata->sess))) + goto end; + + ERR_set_mark(); + /* + * We expect this to fail - because the servername cb will fail. This will + * mark the session as not_resumable. + */ + if (!TEST_false(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE))) { + ERR_clear_last_mark(); + goto end; + } + ERR_pop_to_mark(); + + ret = SSL_TLSEXT_ERR_OK; + end: + SSL_free(serverssl); + SSL_free(clientssl); + cbdata->recurse = 0; + return ret; +} /* * Test multiple resumptions and cache size handling * Test 0: TLSv1.3 (max_early_data set) * Test 1: TLSv1.3 (SSL_OP_NO_TICKET set) * Test 2: TLSv1.3 (max_early_data and SSL_OP_NO_TICKET set) - * Test 3: TLSv1.2 + * Test 3: TLSv1.3 (SSL_OP_NO_TICKET, simultaneous resumes) + * Test 4: TLSv1.2 */ static int test_multi_resume(int idx) { @@ -11528,9 +11578,19 @@ static int test_multi_resume(int idx) SSL_SESSION *sess = NULL; int max_version = TLS1_3_VERSION; int i, testresult = 0; + struct resume_servername_cb_data cbdata; - if (idx == 3) +#if defined(OPENSSL_NO_TLS1_2) + if (idx == 4) + return TEST_skip("TLSv1.2 is disabled in this build"); +#else + if (idx == 4) max_version = TLS1_2_VERSION; +#endif +#if defined(OSSL_NO_USABLE_TLS1_3) + if (idx != 4) + return TEST_skip("No usable TLSv1.3 in this build"); +#endif if (!TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(), TLS_client_method(), TLS1_VERSION, @@ -11546,17 +11606,37 @@ static int test_multi_resume(int idx) if (!TEST_true(SSL_CTX_set_max_early_data(sctx, 1024))) goto end; } - if (idx == 1 || idx == 2) + if (idx == 1 || idx == 2 || idx == 3) SSL_CTX_set_options(sctx, SSL_OP_NO_TICKET); SSL_CTX_sess_set_cache_size(sctx, 5); + if (idx == 3) { + SSL_CTX_set_tlsext_servername_callback(sctx, resume_servername_cb); + SSL_CTX_set_tlsext_servername_arg(sctx, &cbdata); + cbdata.cctx = cctx; + cbdata.sctx = sctx; + cbdata.recurse = 0; + } + for (i = 0; i < 30; i++) { if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl, NULL, NULL)) || !TEST_true(SSL_set_session(clientssl, sess))) goto end; + /* + * Check simultaneous resumes. We pause the connection part way through + * the handshake by (mis)using the servername_cb. The pause occurs after + * session resumption has already occurred, but before any session + * tickets have been issued. While paused we run another complete + * handshake resuming the same session. + */ + if (idx == 3) { + cbdata.i = i; + cbdata.sess = sess; + } + /* * Recreate a bug where dynamically changing the max_early_data value * can cause sessions in the session cache which cannot be deleted. @@ -11916,7 +11996,7 @@ int setup_tests(void) ADD_TEST(test_rstate_string); ADD_ALL_TESTS(test_handshake_retry, 16); ADD_TEST(test_data_retry); - ADD_ALL_TESTS(test_multi_resume, 4); + ADD_ALL_TESTS(test_multi_resume, 5); return 1; err: -- cgit v1.2.3