Skip to content

Commit 9d4968d

Browse files
authored
Merge pull request #11003 from julek-wolfssl/ssl-get-cipher-list-compat
SSL_get_cipher_list: enumerate the configured cipher list
2 parents 2173ec8 + 98e862f commit 9d4968d

4 files changed

Lines changed: 236 additions & 62 deletions

File tree

src/ssl.c

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9305,12 +9305,9 @@ WOLF_STACK_OF(WOLFSSL_CIPHER) *wolfSSL_get_ciphers_compat(const WOLFSSL *ssl)
93059305
if (ssl->suitesStack == NULL)
93069306
return NULL;
93079307

9308-
/* higher priority of cipher suite will be on top of stack */
9309-
#if defined(OPENSSL_ALL)
9310-
for (i = suites->suiteSz - 2; i >=0; i-=2)
9311-
#else
9312-
for (i = 0; i < suites->suiteSz; i+=2)
9313-
#endif
9308+
/* Walk lowest priority first: each suite is inserted at index 0, so
9309+
* the highest priority suite ends up on top of the stack. */
9310+
for (i = suites->suiteSz - 2; i >= 0; i -= 2)
93149311
{
93159312
struct WOLFSSL_CIPHER cipher;
93169313

@@ -9355,6 +9352,49 @@ WOLF_STACK_OF(WOLFSSL_CIPHER) *wolfSSL_get_ciphers_compat(const WOLFSSL *ssl)
93559352
}
93569353
return ssl->suitesStack;
93579354
}
9355+
9356+
/* Get the name of the cipher at index priority in the cipher list configured
9357+
* on this SSL. Index 0 is the highest priority suite. Returns NULL once
9358+
* priority is past the end of the list. Matches OpenSSL SSL_get_cipher_list().
9359+
*/
9360+
const char* wolfSSL_get_cipher_list_compat(const WOLFSSL* ssl, int priority)
9361+
{
9362+
const Suites* suites;
9363+
int i;
9364+
int idx = 0;
9365+
9366+
WOLFSSL_ENTER("wolfSSL_get_cipher_list_compat");
9367+
9368+
if (ssl == NULL || priority < 0)
9369+
return NULL;
9370+
9371+
suites = WOLFSSL_SUITES(ssl);
9372+
if (suites == NULL)
9373+
return NULL;
9374+
9375+
for (i = 0; i < suites->suiteSz; i += 2) {
9376+
/* A couple of suites are placeholders for special options, skip
9377+
* those. */
9378+
if (SCSV_Check(suites->suites[i], suites->suites[i+1])
9379+
|| sslCipherMinMaxCheck(ssl, suites->suites[i],
9380+
suites->suites[i+1])) {
9381+
continue;
9382+
}
9383+
9384+
if (idx++ == priority) {
9385+
/* Report the same name that SSL_CIPHER_get_name() would. */
9386+
#if !defined(WOLFSSL_CIPHER_INTERNALNAME) && \
9387+
!defined(NO_ERROR_STRINGS) && !defined(WOLFSSL_QT)
9388+
return GetCipherNameIana(suites->suites[i], suites->suites[i+1]);
9389+
#else
9390+
return wolfSSL_get_cipher_name_from_suite(suites->suites[i],
9391+
suites->suites[i+1]);
9392+
#endif
9393+
}
9394+
}
9395+
9396+
return NULL;
9397+
}
93589398
#endif /* OPENSSL_EXTRA || OPENSSL_ALL || WOLFSSL_NGINX || WOLFSSL_HAPROXY */
93599399
#ifdef OPENSSL_ALL
93609400
/* returned pointer is to an internal element in WOLFSSL struct and should not

tests/api.c

Lines changed: 176 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -18750,76 +18750,94 @@ static int test_wolfSSL_SSL_CIPHER_find(void)
1875018750
if (EXPECT_SUCCESS() && (wolfSSL_sk_SSL_CIPHER_num(sk) > 1)) {
1875118751
WOLFSSL* sslLtd = NULL;
1875218752
WOLF_STACK_OF(WOLFSSL_CIPHER)* skLtd = NULL;
18753-
const WOLFSSL_CIPHER* keep = wolfSSL_sk_SSL_CIPHER_value(sk, 0);
1875418753
const WOLFSSL_CIPHER* absent = NULL;
1875518754
unsigned char absentId[2] = { 0, 0 };
1875618755
int i;
1875718756

18758-
ExpectNotNull(keep);
18759-
ExpectNotNull(sslLtd = wolfSSL_new(ctx));
18760-
if (keep != NULL) {
18761-
ExpectIntEQ(wolfSSL_set_cipher_list(sslLtd,
18762-
wolfSSL_CIPHER_get_name(keep)), WOLFSSL_SUCCESS);
18763-
}
18764-
ExpectNotNull(skLtd = wolfSSL_get_ciphers_compat(sslLtd));
1876518757
/* Restricting must really drop suites or the check below would be
18766-
* satisfied by the SSL's own list instead of the fallback. */
18767-
ExpectIntLT(wolfSSL_sk_SSL_CIPHER_num(skLtd),
18768-
wolfSSL_sk_SSL_CIPHER_num(sk));
18769-
18770-
/* Find a suite the restricted SSL dropped. */
18771-
for (i = 0; EXPECT_SUCCESS() &&
18758+
* satisfied by the SSL's own list instead of the fallback. Naming a
18759+
* TLS 1.3 suite leaves the TLS 1.2 list alone, so the first suite is
18760+
* not always one that narrows anything - keep the first that does. */
18761+
for (i = 0; EXPECT_SUCCESS() && (skLtd == NULL) &&
1877218762
(i < wolfSSL_sk_SSL_CIPHER_num(sk)); i++) {
18773-
const WOLFSSL_CIPHER* full = wolfSSL_sk_SSL_CIPHER_value(sk, i);
18774-
int inLtd = 0;
18775-
int j;
18763+
const WOLFSSL_CIPHER* keep = wolfSSL_sk_SSL_CIPHER_value(sk, i);
18764+
WOLF_STACK_OF(WOLFSSL_CIPHER)* skTry = NULL;
18765+
WOLFSSL* sslTry = NULL;
1877618766

18777-
if (full == NULL)
18767+
if (keep == NULL)
1877818768
continue;
18779-
for (j = 0; j < wolfSSL_sk_SSL_CIPHER_num(skLtd); j++) {
18780-
const WOLFSSL_CIPHER* ltd =
18781-
wolfSSL_sk_SSL_CIPHER_value(skLtd, j);
18782-
18783-
if ((ltd != NULL) &&
18784-
(ltd->cipherSuite0 == full->cipherSuite0) &&
18785-
(ltd->cipherSuite == full->cipherSuite)) {
18786-
inLtd = 1;
18787-
break;
18788-
}
18769+
ExpectNotNull(sslTry = wolfSSL_new(ctx));
18770+
ExpectIntEQ(wolfSSL_set_cipher_list(sslTry,
18771+
wolfSSL_CIPHER_get_name(keep)), WOLFSSL_SUCCESS);
18772+
ExpectNotNull(skTry = wolfSSL_get_ciphers_compat(sslTry));
18773+
if (EXPECT_SUCCESS() && (wolfSSL_sk_SSL_CIPHER_num(skTry) <
18774+
wolfSSL_sk_SSL_CIPHER_num(sk))) {
18775+
sslLtd = sslTry;
18776+
skLtd = skTry;
1878918777
}
18790-
if (!inLtd) {
18791-
absentId[0] = full->cipherSuite0;
18792-
absentId[1] = full->cipherSuite;
18793-
absent = full;
18794-
break;
18778+
else {
18779+
wolfSSL_free(sslTry);
1879518780
}
1879618781
}
18797-
ExpectNotNull(absent);
1879818782

18799-
ExpectNotNull(found = SSL_CIPHER_find(sslLtd, absentId));
18800-
if (found != NULL) {
18801-
ExpectIntEQ(found->cipherSuite0, absentId[0]);
18802-
ExpectIntEQ(found->cipherSuite, absentId[1]);
18803-
}
18783+
/* Builds whose whole suite list shares one name cannot express a
18784+
* narrower list, so there is nothing to look up here. */
18785+
if (EXPECT_SUCCESS() && (skLtd != NULL)) {
18786+
/* Find a suite the restricted SSL dropped. */
18787+
for (i = 0; EXPECT_SUCCESS() &&
18788+
(i < wolfSSL_sk_SSL_CIPHER_num(sk)); i++) {
18789+
const WOLFSSL_CIPHER* full =
18790+
wolfSSL_sk_SSL_CIPHER_value(sk, i);
18791+
int inLtd = 0;
18792+
int j;
18793+
18794+
if (full == NULL)
18795+
continue;
18796+
for (j = 0; j < wolfSSL_sk_SSL_CIPHER_num(skLtd); j++) {
18797+
const WOLFSSL_CIPHER* ltd =
18798+
wolfSSL_sk_SSL_CIPHER_value(skLtd, j);
18799+
18800+
if ((ltd != NULL) &&
18801+
(ltd->cipherSuite0 == full->cipherSuite0) &&
18802+
(ltd->cipherSuite == full->cipherSuite)) {
18803+
inLtd = 1;
18804+
break;
18805+
}
18806+
}
18807+
if (!inLtd) {
18808+
absentId[0] = full->cipherSuite0;
18809+
absentId[1] = full->cipherSuite;
18810+
absent = full;
18811+
break;
18812+
}
18813+
}
18814+
ExpectNotNull(absent);
1880418815

18805-
#ifdef OPENSSL_ALL
18806-
/* SSL_CIPHER_description(SSL_CIPHER_find(...)) must describe the suite
18807-
* that was looked up. Nothing has been negotiated, so a description
18808-
* taken from the session state would be empty. */
18809-
if ((found != NULL) && (absent != NULL)) {
18810-
char descFind[MAX_DESCRIPTION_SZ];
18811-
char descStack[MAX_DESCRIPTION_SZ];
18816+
ExpectNotNull(found = SSL_CIPHER_find(sslLtd, absentId));
18817+
if (found != NULL) {
18818+
ExpectIntEQ(found->cipherSuite0, absentId[0]);
18819+
ExpectIntEQ(found->cipherSuite, absentId[1]);
18820+
}
1881218821

18813-
XMEMSET(descFind, 0, sizeof(descFind));
18814-
XMEMSET(descStack, 0, sizeof(descStack));
18815-
ExpectNotNull(SSL_CIPHER_description(absent, descStack,
18816-
(int)sizeof(descStack)));
18817-
ExpectNotNull(SSL_CIPHER_description(found, descFind,
18818-
(int)sizeof(descFind)));
18819-
ExpectStrEQ(descFind, descStack);
18820-
ExpectNull(XSTRSTR(descFind, "unknown"));
18821-
}
18822+
#ifdef OPENSSL_ALL
18823+
/* SSL_CIPHER_description(SSL_CIPHER_find(...)) must describe the
18824+
* suite that was looked up. Nothing has been negotiated, so a
18825+
* description taken from the session state would be empty. */
18826+
if ((found != NULL) && (absent != NULL)) {
18827+
char descFind[MAX_DESCRIPTION_SZ];
18828+
char descStack[MAX_DESCRIPTION_SZ];
18829+
18830+
XMEMSET(descFind, 0, sizeof(descFind));
18831+
XMEMSET(descStack, 0, sizeof(descStack));
18832+
ExpectNotNull(SSL_CIPHER_description(absent, descStack,
18833+
(int)sizeof(descStack)));
18834+
ExpectNotNull(SSL_CIPHER_description(found, descFind,
18835+
(int)sizeof(descFind)));
18836+
ExpectStrEQ(descFind, descStack);
18837+
ExpectNull(XSTRSTR(descFind, "unknown"));
18838+
}
1882218839
#endif
18840+
}
1882318841

1882418842
wolfSSL_free(sslLtd);
1882518843
}
@@ -23555,6 +23573,7 @@ static int test_wolfSSL_get_ciphers_compat_empty(void)
2355523573
return EXPECT_RESULT();
2355623574
}
2355723575

23576+
2355823577
static int test_wolfSSL_CTX_ctrl(void)
2355923578
{
2356023579
EXPECT_DECLS;
@@ -23862,6 +23881,103 @@ static int test_wolfSSL_NCONF_negative_paths(void)
2386223881
}
2386323882
#endif /* OPENSSL_ALL */
2386423883

23884+
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL) || \
23885+
defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY)
23886+
/* SSL_get_cipher_list() walks the cipher list configured on the SSL, highest
23887+
* priority first, and returns NULL once past the end. No handshake has run
23888+
* here, so there is no negotiated suite to report. */
23889+
static int test_wolfSSL_get_cipher_list_compat(void)
23890+
{
23891+
EXPECT_DECLS;
23892+
#if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT)
23893+
SSL_CTX* ctx = NULL;
23894+
WOLFSSL* ssl = NULL;
23895+
const char* name = NULL;
23896+
int count = 0;
23897+
23898+
ExpectNotNull(ctx = SSL_CTX_new(SSLv23_client_method()));
23899+
ExpectNotNull(ssl = SSL_new(ctx));
23900+
23901+
ExpectNull(SSL_get_cipher_list(NULL, 0));
23902+
ExpectNull(SSL_get_cipher_list(ssl, -1));
23903+
23904+
ExpectNotNull(name = SSL_get_cipher_list(ssl, 0));
23905+
/* Sentinel differs between the IANA and short-name builds. */
23906+
ExpectStrNE(name, "None");
23907+
ExpectStrNE(name, "NONE");
23908+
23909+
/* Walk to the end of the list. */
23910+
while (EXPECT_SUCCESS() && SSL_get_cipher_list(ssl, count) != NULL)
23911+
count++;
23912+
ExpectIntGT(count, 0);
23913+
ExpectNull(SSL_get_cipher_list(ssl, count));
23914+
23915+
#if defined(OPENSSL_ALL) || defined(WOLFSSL_HAPROXY)
23916+
/* Must agree with the stack SSL_get_ciphers() returns, entry for entry. */
23917+
{
23918+
STACK_OF(SSL_CIPHER)* ciphers = NULL;
23919+
int num = 0;
23920+
int i;
23921+
23922+
ExpectNotNull(ciphers = SSL_get_ciphers(ssl));
23923+
ExpectIntEQ(num = sk_SSL_CIPHER_num(ciphers), count);
23924+
for (i = 0; i < num; i++) {
23925+
ExpectNotNull(name = SSL_get_cipher_list(ssl, i));
23926+
ExpectStrEQ(name,
23927+
SSL_CIPHER_get_name(sk_SSL_CIPHER_value(ciphers, i)));
23928+
}
23929+
}
23930+
#endif
23931+
23932+
SSL_free(ssl);
23933+
ssl = NULL;
23934+
23935+
/* Masking every version filters out all suites, so priority 0 is NULL. */
23936+
ExpectNotNull(ssl = SSL_new(ctx));
23937+
wolfSSL_set_options(ssl, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 |
23938+
SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2 | SSL_OP_NO_TLSv1_3);
23939+
ExpectNull(SSL_get_cipher_list(ssl, 0));
23940+
23941+
SSL_free(ssl);
23942+
SSL_CTX_free(ctx);
23943+
#endif
23944+
return EXPECT_RESULT();
23945+
}
23946+
23947+
/* The list stays the configured suites after a handshake. The old
23948+
* SSL_get_cipher_list() reported the negotiated suite at priority 0. */
23949+
static int test_wolfSSL_get_cipher_list_compat_after_handshake(void)
23950+
{
23951+
EXPECT_DECLS;
23952+
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_TLS) && \
23953+
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
23954+
struct test_memio_ctx test_ctx;
23955+
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
23956+
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
23957+
const char* before = NULL;
23958+
const char* after = NULL;
23959+
23960+
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
23961+
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
23962+
wolfSSLv23_client_method, wolfSSLv23_server_method), 0);
23963+
23964+
ExpectNotNull(before = SSL_get_cipher_list(ssl_c, 0));
23965+
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
23966+
ExpectNotNull(after = SSL_get_cipher_list(ssl_c, 0));
23967+
ExpectStrEQ(before, after);
23968+
23969+
/* More than one suite is still reachable after the handshake. */
23970+
ExpectNotNull(SSL_get_cipher_list(ssl_c, 1));
23971+
23972+
wolfSSL_free(ssl_c);
23973+
wolfSSL_free(ssl_s);
23974+
wolfSSL_CTX_free(ctx_c);
23975+
wolfSSL_CTX_free(ctx_s);
23976+
#endif
23977+
return EXPECT_RESULT();
23978+
}
23979+
#endif /* OPENSSL_EXTRA || OPENSSL_ALL || WOLFSSL_NGINX || WOLFSSL_HAPROXY */
23980+
2386523981
static int test_wolfSSL_d2i_and_i2d_PublicKey(void)
2386623982
{
2386723983
EXPECT_DECLS;
@@ -40742,6 +40858,11 @@ TEST_CASE testCases[] = {
4074240858

4074340859
TEST_DECL(test_wolfSSL_CTX_ctrl),
4074440860
#endif /* OPENSSL_ALL */
40861+
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL) || \
40862+
defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY)
40863+
TEST_DECL(test_wolfSSL_get_cipher_list_compat),
40864+
TEST_DECL(test_wolfSSL_get_cipher_list_compat_after_handshake),
40865+
#endif
4074540866
#if (defined(OPENSSL_ALL) || defined(WOLFSSL_ASIO)) && !defined(NO_RSA)
4074640867
TEST_DECL(test_wolfSSL_CTX_use_certificate_ASN1),
4074740868
#endif /* (OPENSSL_ALL || WOLFSSL_ASIO) && !NO_RSA */

wolfssl/openssl/ssl.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,15 @@ typedef STACK_OF(ACCESS_DESCRIPTION) AUTHORITY_INFO_ACCESS;
254254

255255
#define SSL_get_client_random(ssl,out,outSz) \
256256
wolfSSL_get_client_random((ssl),(out),(outSz))
257-
#define SSL_get_cipher_list(ctx,i) wolfSSL_get_cipher_list_ex((ctx),(i))
257+
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL) || \
258+
defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY)
259+
#define SSL_get_cipher_list(ssl,i) \
260+
wolfSSL_get_cipher_list_compat((ssl),(i))
261+
#else
262+
/* wolfSSL_get_cipher_list_compat is only declared with the compat layer;
263+
* fall back to the always-declared _ex form otherwise. */
264+
#define SSL_get_cipher_list(ssl,i) wolfSSL_get_cipher_list_ex((ssl),(i))
265+
#endif
258266
#define SSL_get_cipher_name(ctx) wolfSSL_get_cipher((ctx))
259267
#define SSL_get_shared_ciphers(ctx,buf,len) \
260268
wolfSSL_get_shared_ciphers((ctx),(buf),(len))

wolfssl/ssl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1463,6 +1463,11 @@ WOLFSSL_API int wolfSSL_set_write_fd (WOLFSSL* ssl, int fd);
14631463
WOLFSSL_API int wolfSSL_set_read_fd (WOLFSSL* ssl, int fd);
14641464
WOLFSSL_API char* wolfSSL_get_cipher_list(int priority);
14651465
WOLFSSL_API char* wolfSSL_get_cipher_list_ex(WOLFSSL* ssl, int priority);
1466+
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL) || \
1467+
defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY)
1468+
WOLFSSL_API const char* wolfSSL_get_cipher_list_compat(const WOLFSSL* ssl,
1469+
int priority);
1470+
#endif
14661471
WOLFSSL_API int wolfSSL_get_ciphers(char* buf, int len);
14671472
WOLFSSL_API int wolfSSL_get_ciphers_iana(char* buf, int len);
14681473
WOLFSSL_API const char* wolfSSL_get_cipher_name(WOLFSSL* ssl);

0 commit comments

Comments
 (0)