Commit 004e9255e4 for openssl.org

commit 004e9255e4e70454c9e9f46dad181d4c6a20c294
Author: Daniel Kubec <kubec@openssl.foundation>
Date:   Thu Apr 2 14:25:29 2026 +0100

    EAP-FAST: echo Session ID on PAC-based session resumption

    Ensure that when a ClientHello includes both a Session ID and a PAC-Opaque
    in the SessionTicket extension, the server echoes the same Session ID in
    the ServerHello if the session is resumed based on the PAC-Opaque.

    Fixes #29095

    Signed-off-by: Daniel Kubec <kubec@openssl.foundation>
    Co-authored-by: Matt Caswell <matt@openssl.foundation>

    Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
    Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
    MergeDate: Thu Apr 16 17:01:03 2026
    (Merged from https://github.com/openssl/openssl/pull/30695)

diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c
index 97455842ec..f061a47f4b 100644
--- a/ssl/statem/statem_srvr.c
+++ b/ssl/statem/statem_srvr.c
@@ -2156,6 +2156,19 @@ static int tls_early_post_process_client_hello(SSL_CONNECTION *s)
             s->peer_ciphers = ciphers;
             s->session->verify_result = X509_V_OK;

+            /*
+             * Per RFC 4851, Section 3.2.2:
+             * If the ClientHello contains both a Session ID and a PAC-Opaque in
+             * the SessionTicket extension, and the server resumes the session
+             * using the PAC-Opaque, it should echo the same Session ID in the
+             * ServerHello.
+             */
+            if (clienthello->session_id_len > 0) {
+                memcpy(s->session->session_id, clienthello->session_id,
+                    clienthello->session_id_len);
+                s->session->session_id_length = clienthello->session_id_len;
+            }
+
             ciphers = NULL;

             /* check if some cipher was preferred by call back */
diff --git a/test/sslapitest.c b/test/sslapitest.c
index 411d007681..9e994062d5 100644
--- a/test/sslapitest.c
+++ b/test/sslapitest.c
@@ -11197,11 +11197,13 @@ static int secret_cb(SSL *s, void *secretin, int *secret_len,
 /*
  * Test the session_secret_cb which is designed for use with EAP-FAST
  */
-static int test_session_secret_cb(void)
+static int test_session_secret_cb(int idx)
 {
     SSL_CTX *cctx = NULL, *sctx = NULL;
     SSL *clientssl = NULL, *serverssl = NULL;
-    SSL_SESSION *secret_sess = NULL;
+    SSL_SESSION *secret_sess = NULL, *server_sess = NULL;
+    unsigned int sess_len;
+    const unsigned char *sessid;
     int testresult = 0;

     if (!TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(),
@@ -11235,12 +11237,20 @@ static int test_session_secret_cb(void)
             NULL, NULL)))
         goto end;

-    /*
-     * No session ids for EAP-FAST - otherwise the state machine gets very
-     * confused.
-     */
-    if (!TEST_true(SSL_SESSION_set1_id(secret_sess, NULL, 0)))
-        goto end;
+    if (idx == 0) {
+        /*
+         * Normal case: no session id
+         */
+        if (!TEST_true(SSL_SESSION_set1_id(secret_sess, NULL, 0)))
+            goto end;
+    } else {
+        /*
+         * Set an explicit session id. Normally we don't support this, but we
+         * can get away with it if we reset the session id later
+         */
+        if (!TEST_true(SSL_SESSION_set1_id(secret_sess, (unsigned char *)"sessionid", 9)))
+            goto end;
+    }

     if (!TEST_true(SSL_set_min_proto_version(clientssl, TLS1_2_VERSION))
         || !TEST_true(SSL_set_max_proto_version(serverssl, TLS1_2_VERSION))
@@ -11251,13 +11261,39 @@ static int test_session_secret_cb(void)
         || !TEST_true(SSL_set_session(clientssl, secret_sess)))
         goto end;

+    if (idx == 1) {
+        /*
+         * We just send the ClientHello here. We expect this to fail with
+         * SSL_ERROR_WANT_READ
+         */
+        if (!TEST_int_le(SSL_connect(clientssl), 0))
+            goto end;
+        /* Reset the session id to avoid confusing the state machine */
+        if (!TEST_true(SSL_SESSION_set1_id(secret_sess, NULL, 0)))
+            goto end;
+    }
     if (!TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)))
         goto end;

+    /* Check that session resumption was successful */
+    if (!TEST_true(SSL_session_reused(clientssl))
+        || !TEST_true(SSL_session_reused(serverssl)))
+        goto end;
+
+    if (idx == 1) {
+        server_sess = SSL_get1_session(serverssl);
+        if (!TEST_ptr(server_sess))
+            goto end;
+        sessid = SSL_SESSION_get_id(server_sess, &sess_len);
+
+        if (!TEST_mem_eq(sessid, sess_len, "sessionid", 9))
+            goto end;
+    }
     testresult = 1;

 end:
     SSL_SESSION_free(secret_sess);
+    SSL_SESSION_free(server_sess);
     SSL_free(serverssl);
     SSL_free(clientssl);
     SSL_CTX_free(sctx);
@@ -14910,7 +14946,7 @@ int setup_tests(void)
 #endif
 #ifndef OPENSSL_NO_TLS1_2
     ADD_TEST(test_ssl_dup);
-    ADD_TEST(test_session_secret_cb);
+    ADD_ALL_TESTS(test_session_secret_cb, 2);
 #ifndef OPENSSL_NO_DH
     ADD_ALL_TESTS(test_set_tmp_dh, 11);
     ADD_ALL_TESTS(test_dh_auto, 7);