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;
}