diff options
| author | Viktor Dukhovni <[email protected]> | 2026-07-11 16:35:56 +0200 |
|---|---|---|
| committer | Norbert Pocs <[email protected]> | 2026-07-21 16:11:36 +0200 |
| commit | 4abb70d32719d15a1db1868484123a1f630b720b (patch) | |
| tree | 34874f4509f1eb4d5cd8430a3932458f5fa4a7b4 | |
| parent | Fix TLS 1.3 PSKs with SSL_VERIFY_PEER and no sid_ctx. (diff) | |
| download | openssl-4abb70d32719d15a1db1868484123a1f630b720b.tar.gz openssl-4abb70d32719d15a1db1868484123a1f630b720b.zip | |
PSK: Handle non-empty client sid_ctx
- Do not reject external PSKs when client-side sid_ctx is set, fixed
by copying the sid_ctx into the pending session.
Tests added for this and also the server side sid_ctx issues.
Reviewed-by: Bob Beck <[email protected]>
Reviewed-by: Norbert Pocs <[email protected]>
MergeDate: Tue Jul 21 14:12:06 2026
(Merged from https://github.com/openssl/openssl/pull/31964)
| -rw-r--r-- | ssl/statem/extensions_clnt.c | 20 | ||||
| -rw-r--r-- | test/sslapitest.c | 163 |
2 files changed, 183 insertions, 0 deletions
diff --git a/ssl/statem/extensions_clnt.c b/ssl/statem/extensions_clnt.c index e3e713f5f65..f17fe2c4959 100644 --- a/ssl/statem/extensions_clnt.c +++ b/ssl/statem/extensions_clnt.c @@ -2415,6 +2415,7 @@ int tls_parse_stoc_psk(SSL_CONNECTION *s, PACKET *pkt, size_t chainidx) { #ifndef OPENSSL_NO_TLS1_3 + SSL_SESSION *sesstmp; unsigned int identity; if (!PACKET_get_net_2(pkt, &identity) || PACKET_remaining(pkt) != 0) { @@ -2456,6 +2457,25 @@ int tls_parse_stoc_psk(SSL_CONNECTION *s, PACKET *pkt, || s->psksession->ext.max_early_data == 0) memcpy(s->early_secret, s->psksession->early_secret, EVP_MAX_MD_SIZE); + /* + * The psk_use_session_cb()/psk_client_callback() may reuse + * the session across connections we can't mutate it directly. + */ + if ((sesstmp = ssl_session_dup(s->psksession, 0)) == NULL) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + SSL_SESSION_free(s->psksession); + s->psksession = sesstmp; + + /* + * s->psksession (now our private copy) was built by the callback, not via + * ssl_get_new_session(), so it was never stamped with our own sid_ctx. Do + * so now, to avoid rejection of the PSK session in tls_process_server_hello(). + */ + memcpy(s->psksession->sid_ctx, s->sid_ctx, s->sid_ctx_length); + s->psksession->sid_ctx_length = s->sid_ctx_length; + SSL_SESSION_free(s->session); s->session = s->psksession; s->psksession = NULL; diff --git a/test/sslapitest.c b/test/sslapitest.c index d751385fd23..6fc18a0dc04 100644 --- a/test/sslapitest.c +++ b/test/sslapitest.c @@ -6546,6 +6546,165 @@ end: return testresult; } +/* + * A server with SSL_VERIFY_PEER set but no session ID context configured + * must still accept a TLS 1.3 external PSK connection: the session was + * just resolved via the application's own callback for this identity, not + * read back out of a shared cache, so the sid_ctx check that guards + * against cross-context cache reuse does not apply to it. + */ +static int test_tls13_psk_verify_peer_no_sid_ctx(void) +{ + SSL_CTX *sctx = NULL, *cctx = NULL; + SSL *serverssl = NULL, *clientssl = NULL; + int testresult = 0; + + if (!TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(), + TLS_client_method(), TLS1_VERSION, 0, &sctx, &cctx, NULL, NULL)) + || !TEST_true(SSL_CTX_set_ciphersuites(cctx, "TLS_AES_128_GCM_SHA256"))) + goto end; + + SSL_CTX_set_verify(sctx, SSL_VERIFY_PEER, NULL); + + SSL_CTX_set_psk_use_session_callback(cctx, use_session_cb); + SSL_CTX_set_psk_find_session_callback(sctx, find_session_cb); + srvid = pskid; + use_session_cb_cnt = 0; + find_session_cb_cnt = 0; + + if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl, + NULL, NULL))) + goto end; + + clientpsk = create_a_psk(clientssl, SHA256_DIGEST_LENGTH); + if (!TEST_ptr(clientpsk) || !TEST_true(SSL_SESSION_up_ref(clientpsk))) + goto end; + serverpsk = clientpsk; + + if (!TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)) + || !TEST_true(SSL_session_reused(clientssl)) + || !TEST_true(SSL_session_reused(serverssl))) + goto end; + + testresult = 1; +end: + SSL_SESSION_free(clientpsk); + SSL_SESSION_free(serverpsk); + clientpsk = serverpsk = NULL; + SSL_free(serverssl); + SSL_free(clientssl); + SSL_CTX_free(sctx); + SSL_CTX_free(cctx); + return testresult; +} + +/* + * A server with SSL_VERIFY_PEER set but no session ID context configured + * must not issue a session ticket after a full (non-PSK) handshake: any + * such ticket would be a poison pill, since resuming it would hit exactly + * the sid_ctx check that a fresh external PSK is exempted from above. + */ +static int test_tls13_psk_verify_peer_no_ticket(void) +{ + SSL_CTX *sctx = NULL, *cctx = NULL; + SSL *serverssl = NULL, *clientssl = NULL; + SSL_SESSION *sess = NULL; + int testresult = 0; + + if (!TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(), + TLS_client_method(), TLS1_VERSION, 0, &sctx, &cctx, cert, privkey))) + goto end; + + SSL_CTX_set_verify(sctx, SSL_VERIFY_PEER, NULL); + + if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl, + NULL, NULL)) + || !TEST_true(create_ssl_connection(serverssl, clientssl, + SSL_ERROR_NONE))) + goto end; + + sess = SSL_get1_session(clientssl); + if (!TEST_ptr(sess) || !TEST_false(SSL_SESSION_has_ticket(sess))) + goto end; + + testresult = 1; +end: + SSL_SESSION_free(sess); + SSL_free(serverssl); + SSL_free(clientssl); + SSL_CTX_free(sctx); + SSL_CTX_free(cctx); + return testresult; +} + +/* + * A client with its own session ID context configured must still be able + * to resume a TLS 1.3 external PSK obtained via the legacy + * psk_use_session_cb()/psk_client_callback() callbacks. s->psksession is + * never routed through ssl_get_new_session(), so, unlike an ordinary + * session, it was never stamped with the client's own sid_ctx; without + * that stamp tls_process_server_hello()'s own sid_ctx self-consistency + * check fatally rejects marking it reused. + * + * Test 0: new style callback (psk_use_session_cb()/psk_find_session_cb()). + * Test 1: old style callback (psk_client_callback()/psk_server_callback()). + */ +static int test_tls13_psk_client_sid_ctx(int idx) +{ + SSL_CTX *sctx = NULL, *cctx = NULL; + SSL *serverssl = NULL, *clientssl = NULL; + int sess_id_ctx = 1; + int testresult = 0; + + if (!TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(), + TLS_client_method(), TLS1_VERSION, 0, &sctx, &cctx, NULL, NULL)) + || !TEST_true(SSL_CTX_set_ciphersuites(cctx, "TLS_AES_128_GCM_SHA256")) + || !TEST_true(SSL_CTX_set_session_id_context(cctx, + (void *)&sess_id_ctx, sizeof(sess_id_ctx)))) + goto end; + + srvid = pskid; + if (idx == 0) { + SSL_CTX_set_psk_use_session_callback(cctx, use_session_cb); + SSL_CTX_set_psk_find_session_callback(sctx, find_session_cb); + use_session_cb_cnt = 0; + find_session_cb_cnt = 0; + } +#ifndef OPENSSL_NO_PSK + else { + SSL_CTX_set_psk_client_callback(cctx, psk_client_cb); + SSL_CTX_set_psk_server_callback(sctx, psk_server_cb); + psk_client_cb_cnt = 0; + psk_server_cb_cnt = 0; + } +#endif + + if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl, + NULL, NULL))) + goto end; + + clientpsk = create_a_psk(clientssl, SHA256_DIGEST_LENGTH); + if (!TEST_ptr(clientpsk) || !TEST_true(SSL_SESSION_up_ref(clientpsk))) + goto end; + serverpsk = clientpsk; + + if (!TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)) + || !TEST_true(SSL_session_reused(clientssl)) + || !TEST_true(SSL_session_reused(serverssl))) + goto end; + + testresult = 1; +end: + SSL_SESSION_free(clientpsk); + SSL_SESSION_free(serverpsk); + clientpsk = serverpsk = NULL; + SSL_free(serverssl); + SSL_free(clientssl); + SSL_CTX_free(sctx); + SSL_CTX_free(cctx); + return testresult; +} + #ifndef OSSL_NO_USABLE_TLS1_3 /* * Test TLS1.3 connection establishment succeeds with various configurations of @@ -15325,9 +15484,13 @@ int setup_tests(void) ADD_ALL_TESTS(test_tls13_ciphersuite, 4); #ifdef OPENSSL_NO_PSK ADD_ALL_TESTS(test_tls13_psk, 1); + ADD_ALL_TESTS(test_tls13_psk_client_sid_ctx, 1); #else ADD_ALL_TESTS(test_tls13_psk, 4); + ADD_ALL_TESTS(test_tls13_psk_client_sid_ctx, 2); #endif /* OPENSSL_NO_PSK */ + ADD_TEST(test_tls13_psk_verify_peer_no_sid_ctx); + ADD_TEST(test_tls13_psk_verify_peer_no_ticket); #ifndef OSSL_NO_USABLE_TLS1_3 ADD_ALL_TESTS(test_tls13_no_dhe_kex, 8); #endif /* OSSL_NO_USABLE_TLS1_3 */ |
