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 <nhorman@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/24042)
This commit is contained in:
Matt Caswell 2024-03-05 15:35:51 +00:00 committed by Tomas Mraz
parent 0447cd690f
commit cfeaf33a26

View File

@ -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: