Commit dd68de6790 for openssl.org
commit dd68de679008e4a581561bc756e5ad1e8e85e9a1
Author: Matt Caswell <matt@openssl.foundation>
Date: Mon Nov 2 12:03:20 2026 +0000
ssl/ssl_lib.c: fail SSL_copy_session_id() if the provider sigalg counts differ
SSL_copy_session_id(t, f) makes t share f's CERT, but t keeps its own
SSL_CTX and ssl_pkey_num. If the two SSL_CTXs have different numbers
of provider signature algorithms, the sig_idx values from t's SSL_CTX
no longer match the CERT's pkeys array, and a later handshake on t reads
past its end.
Refreshing ssl_pkey_num is not enough: t's SSL_CTX would still hand out
sig_idx values beyond it, and valid_flags would be allocated too small.
Copying between such SSL_CTXs makes no sense, so fail before t is
modified.
Resolves: https://github.com/openssl/openssl/issues/33459
Reported-by: Bhabani Sankar Das <007bsd@proton.me>
Reported-by: Anthropic's OSS Scanner (finding ANT-2026-8A5XNKF9)
Assisted-by: Claude Code:claude-opus-5-5
Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
Merge-date: Fri Nov 6 12:55:13 2026
Merged-from: https://github.com/openssl/openssl/pull/33489
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 4eb3e584e8..de0309d539 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -2223,6 +2223,15 @@ int SSL_copy_session_id(SSL *t, const SSL *f)
if (tsc == NULL || fsc == NULL)
return 0;
+ /*
+ * |t| keeps its own SSL_CTX, whose provider sigalgs index the CERT's
+ * pkeys, so it cannot share a CERT sized for a different set of them.
+ */
+ if (tsc->ssl_pkey_num != fsc->cert->ssl_pkey_num) {
+ ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
+
/* Do we need to do SSL locking? */
if (!SSL_set_session(t, SSL_get_session(f))) {
return 0;
diff --git a/test/sslapitest.c b/test/sslapitest.c
index 46b0fab710..933d58fbcb 100644
--- a/test/sslapitest.c
+++ b/test/sslapitest.c
@@ -15347,6 +15347,48 @@ end:
}
#endif /* OPENSSL_NO_TLS1_2 */
+/*
+ * SSL_copy_session_id() makes |t| share |f|'s CERT, but |t| keeps its own
+ * SSL_CTX. It must refuse a CERT sized for a different number of provider
+ * sigalgs, and leave |t| unchanged when it does.
+ */
+static int test_copy_session_id_ctx_mismatch(void)
+{
+ OSSL_PROVIDER *tlsprov = NULL;
+ SSL_CTX *sctx = NULL, *newsctx = NULL, *cctx = NULL;
+ SSL *from = NULL, *same = NULL, *to = NULL;
+ SSL_CONNECTION *tosc;
+ CERT *tocert;
+ int testresult = 0;
+
+ if (!sigalg_ctx_switch_setup(&tlsprov, 0, &sctx, &newsctx, &cctx))
+ goto end;
+
+ if (!TEST_ptr(from = SSL_new(sctx))
+ || !TEST_ptr(same = SSL_new(sctx))
+ || !TEST_ptr(to = SSL_new(newsctx))
+ || !TEST_ptr(tosc = SSL_CONNECTION_FROM_SSL_ONLY(to)))
+ goto end;
+ tocert = tosc->cert;
+
+ if (!TEST_false(SSL_copy_session_id(to, from))
+ || !TEST_ptr_eq(tosc->cert, tocert)
+ || !TEST_true(SSL_copy_session_id(same, from)))
+ goto end;
+
+ testresult = 1;
+end:
+ SSL_free(from);
+ SSL_free(same);
+ SSL_free(to);
+ SSL_CTX_free(sctx);
+ SSL_CTX_free(newsctx);
+ SSL_CTX_free(cctx);
+ OSSL_PROVIDER_unload(tlsprov);
+
+ return testresult;
+}
+
/*
* The ClientHello raw extension array is sized when the ClientHello is
* collected. Switching to an SSL_CTX with more custom extensions from the
@@ -19745,6 +19787,7 @@ int setup_tests(void)
ADD_ALL_TESTS(test_sigalg_ctx_switch, 4);
ADD_ALL_TESTS(test_ctx_switch_more_custom_exts, 2);
ADD_ALL_TESTS(test_sigalg_ctx_switch_cert_cb, 2);
+ ADD_TEST(test_copy_session_id_ctx_mismatch);
#ifndef OPENSSL_NO_TLS1_2
ADD_TEST(test_sigalg_ctx_switch_reneg);
#endif