Commit 51b0bd6097 for openssl.org

commit 51b0bd6097e57cf4ff863c272b611dedc4ecf8d0
Author: Frederik Wedel-Heinen <frederik.wedel-heinen@dencrypt.dk>
Date:   Sat Oct 11 07:23:50 2025 +0200

    SSL_get_shared_ciphers(): Return NUL-terminated buffer for no shared ciphers

    Also validate the input buffer and length properly.

    Reviewed-by: Matt Caswell <matt@openssl.org>
    Reviewed-by: Paul Dale <paul.dale@oracle.com>
    Reviewed-by: Tomas Mraz <tomas@openssl.org>
    MergeDate: Wed Mar  4 09:30:18 2026
    (Merged from https://github.com/openssl/openssl/pull/28859)

diff --git a/doc/man3/SSL_get_ciphers.pod b/doc/man3/SSL_get_ciphers.pod
index 307f27e52d..d8481cf7a7 100644
--- a/doc/man3/SSL_get_ciphers.pod
+++ b/doc/man3/SSL_get_ciphers.pod
@@ -76,6 +76,8 @@ description of SSL_get1_supported_ciphers() above). This function will return
 available shared ciphersuites whether or not they are enabled. This is a server
 side function only and must only be called after the completion of the initial
 handshake.
+The function sets an empty string when B<ssl> fails the handshake due to the
+absence of shared ciphers.

 =head1 NOTES

diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 76615f4809..3d21a68364 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -3561,22 +3561,21 @@ char *SSL_get_shared_ciphers(const SSL *s, char *buf, int size)
     int i;
     const SSL_CONNECTION *sc = SSL_CONNECTION_FROM_CONST_SSL(s);

-    if (sc == NULL)
+    if (size < 2 || buf == NULL)
         return NULL;

-    if (!sc->server
-        || sc->peer_ciphers == NULL
-        || size < 2)
+    buf[0] = '\0';
+
+    if (sc == NULL || !sc->server)
         return NULL;

     p = buf;
     clntsk = sc->peer_ciphers;
     srvrsk = SSL_get_ciphers(s);
-    if (clntsk == NULL || srvrsk == NULL)
-        return NULL;

-    if (sk_SSL_CIPHER_num(clntsk) == 0 || sk_SSL_CIPHER_num(srvrsk) == 0)
-        return NULL;
+    if (clntsk == NULL || sk_SSL_CIPHER_num(clntsk) == 0
+        || srvrsk == NULL || sk_SSL_CIPHER_num(srvrsk) == 0)
+        return buf;

     for (i = 0; i < sk_SSL_CIPHER_num(clntsk); i++) {
         int n;
@@ -3596,10 +3595,9 @@ char *SSL_get_shared_ciphers(const SSL *s, char *buf, int size)
     }

     /* No overlap */
-    if (p == buf)
-        return NULL;
+    if (p != buf)
+        p[-1] = '\0';

-    p[-1] = '\0';
     return buf;
 }

diff --git a/test/sslapitest.c b/test/sslapitest.c
index 6b6547ed84..3409085475 100644
--- a/test/sslapitest.c
+++ b/test/sslapitest.c
@@ -8762,6 +8762,13 @@ static struct {
         NULL,
         "AES128-SHA",
         "AES128-SHA" },
+    { TLS1_2_VERSION,
+        "AES256-SHA",
+        NULL,
+        "AES128-SHA",
+        NULL,
+        "",
+        "" },
 #endif
 /*
  * This test combines TLSv1.3 and TLSv1.2 ciphersuites so they must both be
@@ -8786,6 +8793,13 @@ static struct {
         "TLS_AES_256_GCM_SHA384",
         "TLS_AES_256_GCM_SHA384",
         "TLS_AES_256_GCM_SHA384" },
+    { TLS1_3_VERSION,
+        "AES128-SHA",
+        "TLS_AES_128_GCM_SHA256",
+        "AES256-SHA",
+        "TLS_AES_256_GCM_SHA384",
+        "",
+        "" },
 #endif
 };

@@ -8796,6 +8810,9 @@ static int int_test_ssl_get_shared_ciphers(int tst, int clnt)
     int testresult = 0;
     char buf[1024];
     OSSL_LIB_CTX *tmplibctx = OSSL_LIB_CTX_new();
+    const char *expbuf = is_fips ? shared_ciphers_data[tst].fipsshared
+                                 : shared_ciphers_data[tst].shared;
+    int handshakeok = strcmp(expbuf, "") != 0;

     if (!TEST_ptr(tmplibctx))
         goto end;
@@ -8836,18 +8853,22 @@ static int int_test_ssl_get_shared_ciphers(int tst, int clnt)
                 shared_ciphers_data[tst].srvrtls13ciphers))))
         goto end;

-    if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl,
-            NULL, NULL))
-        || !TEST_true(create_ssl_connection(serverssl, clientssl,
-            SSL_ERROR_NONE)))
+    if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl, NULL,
+            NULL)))
         goto end;

+    if (handshakeok) {
+        if (!TEST_true(create_ssl_connection(serverssl, clientssl,
+                SSL_ERROR_NONE)))
+            goto end;
+    } else {
+        if (!TEST_false(create_ssl_connection(serverssl, clientssl,
+                SSL_ERROR_NONE)))
+            goto end;
+    }
+
     if (!TEST_ptr(SSL_get_shared_ciphers(serverssl, buf, sizeof(buf)))
-        || !TEST_int_eq(strcmp(buf,
-                            is_fips
-                                ? shared_ciphers_data[tst].fipsshared
-                                : shared_ciphers_data[tst].shared),
-            0)) {
+        || !TEST_int_eq(strcmp(buf, expbuf), 0)) {
         TEST_info("Shared ciphers are: %s\n", buf);
         goto end;
     }