Commit 33eaaad65b for openssl.org

commit 33eaaad65ba7623345a6dcf7a20612dc0339107e
Author: Alberto Maschietto <albertomaschietto9@gmail.com>
Date:   Wed Aug 19 21:42:03 2026 +0400

    Fix NULL dereference in SSL_get0_group_name() before a handshake

    SSL_get0_group_name() falls back to sc->session->kex_group whenever the
    connection is not a TLSv1.3 one that has already done a key exchange.
    On a connection that has not started (or completed) a handshake yet,
    sc->session is still NULL, so the fallback crashes instead of returning
    NULL as the manual page promises.

    This is the same problem that was fixed for SSL_get_negotiated_group() in
    commit 4ca80d3941, and the fix here mirrors it: only consult the session
    when there is one, and report "no group" otherwise. It affects TLS, DTLS
    and QUIC connections, and every branch since the accessor was added in
    3.2.

    The RETURN VALUES section said a NULL return meant an error occurred.
    That is not accurate: NULL simply means no group is available, which is
    the normal answer before a handshake or after one that used no key
    agreement group. Reword it accordingly.

    Add a regression test that calls both group accessors on a fresh SSL
    object. Without the fix it segfaults; with it, SSL_get0_group_name()
    returns NULL and SSL_get_negotiated_group() returns NID_undef.

    Fixes #32379

    Assisted-by: Claude Code:claude-opus-5
    Reviewed-by: Frederik Wedel-Heinen <fwh.openssl@gmail.com>
    Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
    Merge-date: Tue Sep 29 17:04:02 2026
    Merged-from: https://github.com/openssl/openssl/pull/32437

diff --git a/doc/man3/SSL_get0_group_name.pod b/doc/man3/SSL_get0_group_name.pod
index 37bea2fc7b..574a6be14c 100644
--- a/doc/man3/SSL_get0_group_name.pod
+++ b/doc/man3/SSL_get0_group_name.pod
@@ -20,8 +20,9 @@ the key agreement of the current TLS session establishment.

 If non-NULL, SSL_get0_group_name() returns the name of the group that was used for
 the key agreement of the current TLS session establishment.
-If SSL_get0_group_name() returns NULL, an error occurred; possibly no TLS session
-has been established. See also L<SSL_get_negotiated_group(3)>.
+SSL_get0_group_name() returns NULL if no group is available, for example
+because no TLS session has been established yet, or because the handshake did
+not use a key agreement group. See also L<SSL_get_negotiated_group(3)>.

 Note that the return value is valid only during the lifetime of the
 SSL object I<ssl>.
@@ -37,7 +38,7 @@ This function was added in OpenSSL 3.2.

 =head1 COPYRIGHT

-Copyright 2023-2025 The OpenSSL Project Authors. All Rights Reserved.
+Copyright 2023-2026 The OpenSSL Project Authors. All Rights Reserved.

 Licensed under the Apache License 2.0 (the "License").  You may not use
 this file except in compliance with the License.  You can obtain a copy
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index af320478bf..05ac3e493d 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -5671,8 +5671,10 @@ const char *SSL_get0_group_name(SSL *s)

     if (SSL_CONNECTION_IS_VERSION13(sc) && sc->s3.did_kex)
         id = sc->s3.group_id;
-    else
+    else if (sc->session != NULL)
         id = sc->session->kex_group;
+    else
+        return NULL;

     return tls1_group_id2name(s->ctx, id);
 }
diff --git a/test/sslapitest.c b/test/sslapitest.c
index 697c3d5b83..fc43d6af0a 100644
--- a/test/sslapitest.c
+++ b/test/sslapitest.c
@@ -8433,6 +8433,37 @@ end:
 #endif /* OSSL_NO_USABLE_TLS1_3 */
 #endif /* !defined(OSSL_NO_USABLE_TLS1_3) || !defined(OSSL_NO_USABLE_DTLS1_3) */

+/*
+ * Test that the group accessors report "no group" rather than crashing when
+ * they are called on a connection that has not performed a handshake yet.
+ */
+#if !defined(OPENSSL_NO_TLS1_2) || !defined(OSSL_NO_USABLE_TLS1_3)
+static int test_group_before_handshake(void)
+{
+    SSL_CTX *cctx = NULL;
+    SSL *clientssl = NULL;
+    int testresult = 0;
+
+    if (!TEST_ptr(cctx = SSL_CTX_new_ex(libctx, NULL, TLS_client_method())))
+        goto end;
+
+    if (!TEST_ptr(clientssl = SSL_new(cctx)))
+        goto end;
+
+    if (!TEST_ptr_null(SSL_get0_group_name(clientssl)))
+        goto end;
+
+    if (!TEST_int_eq(SSL_get_negotiated_group(clientssl), NID_undef))
+        goto end;
+
+    testresult = 1;
+end:
+    SSL_free(clientssl);
+    SSL_CTX_free(cctx);
+    return testresult;
+}
+#endif
+
 static int clntaddoldcb = 0;
 static int clntparseoldcb = 0;
 static int srvaddoldcb = 0;
@@ -18030,6 +18061,9 @@ int setup_tests(void)
 #ifndef OSSL_NO_USABLE_TLS1_3
     ADD_TEST(test_ktls_moving_write_buffer);
 #endif
+#endif
+#if !defined(OPENSSL_NO_TLS1_2) || !defined(OSSL_NO_USABLE_TLS1_3)
+    ADD_TEST(test_group_before_handshake);
 #endif
     ADD_TEST(test_large_message_tls);
     ADD_TEST(test_large_message_tls_read_ahead);