Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions src/ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -10997,6 +10997,44 @@ const WOLFSSL_CIPHER* wolfSSL_get_cipher_by_value(word16 value)
return cipher;
}

#if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || \
defined(WOLFSSL_HAPROXY) || defined(OPENSSL_EXTRA) || defined(HAVE_LIGHTY)
/* Locate a cipher in the SSL's cipher list by 2-byte wire-format suite id.
*
* Returned pointer references storage owned by the SSL object's internal
* cipher list; callers must not free it. It remains valid until SSL_free.
Comment thread
Roy-Carter marked this conversation as resolved.
*
* @param [in] ssl SSL/TLS object whose cipher list is searched.
* @param [in] ptr Pointer to a 2-byte cipher suite identifier.
* @return Matching cipher on success.
* @return NULL if ssl or ptr is NULL, or no cipher matches.
*/
const WOLFSSL_CIPHER* wolfSSL_SSL_CIPHER_find(WOLFSSL* ssl,
const unsigned char* ptr)
{
WOLF_STACK_OF(WOLFSSL_CIPHER)* sk;
WOLFSSL_STACK* node;

WOLFSSL_ENTER("wolfSSL_SSL_CIPHER_find");

if (ssl == NULL || ptr == NULL)
return NULL;

sk = wolfSSL_get_ciphers_compat(ssl);
if (sk == NULL)
return NULL;

for (node = sk; node != NULL; node = node->next) {
if (node->data.cipher.cipherSuite0 == ptr[0] &&
node->data.cipher.cipherSuite == ptr[1]) {
return &node->data.cipher;
}
}

return NULL;
}
#endif


#if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || defined(HAVE_CURVE448) || \
!defined(NO_DH) || (defined(WOLFSSL_TLS13) && defined(WOLFSSL_HAVE_MLKEM))
Expand Down
38 changes: 38 additions & 0 deletions src/ssl_load.c
Original file line number Diff line number Diff line change
Expand Up @@ -5220,6 +5220,8 @@ int wolfSSL_add0_chain_cert(WOLFSSL* ssl, WOLFSSL_X509* x509)
if (ret == 1) {
/* We now own cert chain. */
ssl->buffers.weOwnCertChain = 1;
/* Account for the certificate just added to the chain. */
ssl->buffers.certChainCnt++;
/* Create a stack to put certificate into. */
if (ssl->ourCertChain == NULL) {
ssl->ourCertChain = wolfSSL_sk_X509_new_null();
Expand Down Expand Up @@ -5270,6 +5272,42 @@ int wolfSSL_add1_chain_cert(WOLFSSL* ssl, WOLFSSL_X509* x509)

return ret;
}

/* Clear all extra chain certificates set on the SSL object.
*
* Mirrors OpenSSL's SSL_clear_chain_certs(): frees any chain certificates
* previously added via SSL_add0_chain_cert / SSL_add1_chain_cert (or set via
* SSL_set0_chain / SSL_set1_chain) on this SSL. Does not affect the leaf
* certificate, the private key, or chain certificates inherited from the
* WOLFSSL_CTX.
*
* @param [in, out] ssl SSL object.
* @return 1 on success.
* @return 0 when ssl is NULL.
*/
int wolfSSL_clear_chain_certs(WOLFSSL* ssl)
{
WOLFSSL_ENTER("wolfSSL_clear_chain_certs");

if (ssl == NULL)
return 0;

/* Free the DER-encoded chain buffer if this SSL owns it. */
if (ssl->buffers.weOwnCertChain) {
FreeDer(&ssl->buffers.certChain);
ssl->buffers.weOwnCertChain = 0;
}
ssl->buffers.certChain = NULL;
ssl->buffers.certChainCnt = 0;

/* Free the X509 stack used to track ownership of added chain certs. */
if (ssl->ourCertChain != NULL) {
wolfSSL_sk_X509_pop_free(ssl->ourCertChain, NULL);
ssl->ourCertChain = NULL;
}

return 1;
}
#endif /* KEEP_OUR_CERT */
#endif /* OPENSSL_EXTRA, HAVE_LIGHTY, WOLFSSL_MYSQL_COMPATIBLE, HAVE_STUNNEL,
WOLFSSL_NGINX, HAVE_POCO_LIB, WOLFSSL_HAPROXY */
Expand Down
38 changes: 38 additions & 0 deletions src/ssl_sk.c
Original file line number Diff line number Diff line change
Expand Up @@ -1205,6 +1205,44 @@ void wolfSSL_sk_SSL_CIPHER_free(WOLF_STACK_OF(WOLFSSL_CIPHER)* sk)
WOLFSSL_ENTER("wolfSSL_sk_SSL_CIPHER_free");
wolfSSL_sk_free(sk);
}

/* Remove the cipher at the given index from the stack.
*
* @param [in,out] sk Stack of ciphers.
* @param [in] idx Index of cipher to remove.
* @return Heap copy of removed cipher on success.
* @return NULL on failure.
*/
WOLFSSL_CIPHER* wolfSSL_sk_SSL_CIPHER_delete(
WOLF_STACK_OF(WOLFSSL_CIPHER)* sk, int idx)
{
WOLFSSL_CIPHER* ret = NULL;
WOLFSSL_CIPHER* cipher;

WOLFSSL_ENTER("wolfSSL_sk_SSL_CIPHER_delete");

if (sk == NULL || idx < 0)
return NULL;

/* Capture the inline cipher value before the pop_node call frees the
* underlying memory. */
cipher = wolfSSL_sk_SSL_CIPHER_value(sk, idx);
if (cipher == NULL)
return NULL;

ret = (WOLFSSL_CIPHER*)XMALLOC(sizeof(WOLFSSL_CIPHER), NULL,
DYNAMIC_TYPE_OPENSSL);
if (ret == NULL)
return NULL;

*ret = *cipher;

/* pop_node returns NULL for STACK_TYPE_CIPHER (data is static/inline),
* but it still performs the unlink and node free that we need. */
(void)wolfSSL_sk_pop_node(sk, idx);

return ret;
}
#endif /* OPENSSL_ALL || OPENSSL_EXTRA */

/*******************************************************************************
Expand Down
211 changes: 211 additions & 0 deletions tests/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -3694,6 +3694,138 @@ static int test_wolfSSL_CTX_add1_chain_cert(void)
return EXPECT_RESULT();
}

/* Test SSL_clear_chain_certs: must drop chain certs added via add0/add1,
* leave leaf certificate intact, and tolerate repeated calls / NULL input. */
static int test_wolfSSL_clear_chain_certs(void)
{
EXPECT_DECLS;
#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && defined(OPENSSL_EXTRA) && \
defined(KEEP_OUR_CERT) && !defined(NO_RSA) && !defined(NO_TLS) && \
!defined(NO_WOLFSSL_CLIENT)
WOLFSSL_CTX* ctx = NULL;
WOLFSSL* ssl = NULL;
WOLFSSL_X509* x509 = NULL;
WOLF_STACK_OF(X509)* chain = NULL;
const char* chainCerts[] = {
"./certs/intermediate/ca-int2-cert.pem",
"./certs/intermediate/ca-int-cert.pem",
NULL
};
const char** cert;

/* NULL arg. */
ExpectIntEQ(SSL_clear_chain_certs(NULL), 0);

ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()));
ExpectNotNull(ssl = wolfSSL_new(ctx));

/* Clear on an SSL with no chain is a no-op success. */
ExpectIntEQ(SSL_clear_chain_certs(ssl), 1);

/* Set leaf so subsequent adds go to the chain. */
ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(
"./certs/intermediate/client-int-cert.pem", WOLFSSL_FILETYPE_PEM));
ExpectIntEQ(SSL_add1_chain_cert(ssl, x509), 1);
wolfSSL_X509_free(x509);
x509 = NULL;

for (cert = chainCerts; EXPECT_SUCCESS() && *cert != NULL; cert++) {
ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(*cert,
WOLFSSL_FILETYPE_PEM));
ExpectIntEQ(SSL_add1_chain_cert(ssl, x509), 1);
wolfSSL_X509_free(x509);
x509 = NULL;
}

/* Chain populated with the 2 intermediates. */
ExpectIntEQ(SSL_get0_chain_certs(ssl, &chain), 1);
ExpectIntEQ(sk_X509_num(chain), 2);
if (ssl != NULL) {
ExpectIntEQ(ssl->buffers.certChainCnt, 2);
ExpectNotNull(ssl->buffers.certChain);
ExpectNotNull(ssl->ourCertChain);
}

/* Clear. */
ExpectIntEQ(SSL_clear_chain_certs(ssl), 1);
if (ssl != NULL) {
ExpectNull(ssl->buffers.certChain);
ExpectNull(ssl->ourCertChain);
ExpectIntEQ(ssl->buffers.weOwnCertChain, 0);
/* Leaf untouched. */
ExpectNotNull(ssl->ourCert);
}
chain = NULL;
ExpectIntEQ(SSL_get0_chain_certs(ssl, &chain), 1);
/* Like OpenSSL, the chain is emptied (NULL) after a clear. */
ExpectNull(chain);

/* Idempotent: clearing again still succeeds. */
ExpectIntEQ(SSL_clear_chain_certs(ssl), 1);

/* Re-adding after clear works. */
ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(
"./certs/intermediate/ca-int2-cert.pem", WOLFSSL_FILETYPE_PEM));
ExpectIntEQ(SSL_add1_chain_cert(ssl, x509), 1);
wolfSSL_X509_free(x509);
chain = NULL;
ExpectIntEQ(SSL_get0_chain_certs(ssl, &chain), 1);
ExpectIntEQ(sk_X509_num(chain), 1);

SSL_free(ssl);
SSL_CTX_free(ctx);
#endif
return EXPECT_RESULT();
}

#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && defined(OPENSSL_EXTRA) && \
defined(KEEP_OUR_CERT) && !defined(NO_RSA) && !defined(NO_TLS) && \
!defined(NO_WOLFSSL_SERVER)
/* Server-side ssl_ready hook: add chain certs then clear them, so the
* handshake runs against a freshly-cleared chain state. */
static int test_wolfSSL_clear_chain_certs_handshake_ssl_ready(WOLFSSL* ssl)
{
EXPECT_DECLS;
WOLFSSL_X509* x509 = NULL;

ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(
"./certs/intermediate/ca-int2-cert.pem", WOLFSSL_FILETYPE_PEM));
ExpectIntEQ(SSL_add1_chain_cert(ssl, x509), 1);
wolfSSL_X509_free(x509);

/* Drop the chain again; connection must still complete afterwards. */
ExpectIntEQ(SSL_clear_chain_certs(ssl), 1);

return EXPECT_RESULT();
}
#endif

/* Test that a connection still completes after SSL_clear_chain_certs. */
static int test_wolfSSL_clear_chain_certs_handshake(void)
{
EXPECT_DECLS;
#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && defined(OPENSSL_EXTRA) && \
defined(KEEP_OUR_CERT) && !defined(NO_RSA) && !defined(NO_TLS) && \
!defined(NO_WOLFSSL_SERVER) && !defined(NO_WOLFSSL_CLIENT)
test_ssl_cbf client_cbs;
test_ssl_cbf server_cbs;

XMEMSET(&client_cbs, 0, sizeof(client_cbs));
XMEMSET(&server_cbs, 0, sizeof(server_cbs));

client_cbs.method = wolfTLS_client_method;
server_cbs.method = wolfTLS_server_method;

server_cbs.ssl_ready = test_wolfSSL_clear_chain_certs_handshake_ssl_ready;

/* nofail_memio runs the full handshake and a read/write exchange, so a
* successful return proves the connection completed after the clear. */
ExpectIntEQ(test_wolfSSL_client_server_nofail_memio(&client_cbs,
&server_cbs, NULL), TEST_SUCCESS);
#endif
return EXPECT_RESULT();
}

/* Test that wolfssl_add_to_chain rejects sizes that would overflow word32.
* ZD #21241 */
static int test_wolfSSL_add_to_chain_overflow(void)
Expand Down Expand Up @@ -17178,6 +17310,26 @@ static int test_wolfSSL_sk_SSL_CIPHER(void)

/* error case because connection has not been established yet */
ExpectIntEQ(sk_SSL_CIPHER_find(sk, SSL_get_current_cipher(ssl)), -1);

/* Exercise sk_SSL_CIPHER_delete on the duplicated stack so we don't
* disturb the SSL object's internal cipher list. */
{
int dupNum = sk_SSL_CIPHER_num(dupSk);
if (dupNum > 0) {
SSL_CIPHER* removed = NULL;

/* Out-of-range and negative idx should return NULL. */
ExpectNull(sk_SSL_CIPHER_delete(dupSk, -1));
ExpectNull(sk_SSL_CIPHER_delete(dupSk, dupNum));
ExpectNull(sk_SSL_CIPHER_delete(NULL, 0));

/* Delete the head element and verify count decreased. */
ExpectNotNull(removed = sk_SSL_CIPHER_delete(dupSk, 0));
ExpectIntEQ(sk_SSL_CIPHER_num(dupSk), dupNum - 1);
XFREE(removed, NULL, DYNAMIC_TYPE_OPENSSL);
}
}

sk_SSL_CIPHER_free(dupSk);

/* sk is pointer to internal struct that should be free'd in SSL_free */
Expand All @@ -17189,6 +17341,62 @@ static int test_wolfSSL_sk_SSL_CIPHER(void)
return EXPECT_RESULT();
}

static int test_wolfSSL_SSL_CIPHER_find(void)
{
EXPECT_DECLS;
#if (defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA) || \
defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) || \
defined(HAVE_LIGHTY)) && \
!defined(NO_CERTS) && !defined(NO_TLS) && !defined(NO_FILESYSTEM) && \
!defined(NO_RSA) && \
(!defined(NO_WOLFSSL_CLIENT) || !defined(NO_WOLFSSL_SERVER))
SSL* ssl = NULL;
SSL_CTX* ctx = NULL;
STACK_OF(SSL_CIPHER)* sk = NULL;
const SSL_CIPHER* found = NULL;
unsigned char id[2];
const unsigned char bogus[2] = { 0xFF, 0xFF };

#ifndef NO_WOLFSSL_SERVER
ExpectNotNull(ctx = SSL_CTX_new(wolfSSLv23_server_method()));
#else
ExpectNotNull(ctx = SSL_CTX_new(wolfSSLv23_client_method()));
#endif
ExpectTrue(SSL_CTX_use_certificate_file(ctx, svrCertFile, SSL_FILETYPE_PEM));
ExpectTrue(SSL_CTX_use_PrivateKey_file(ctx, svrKeyFile, SSL_FILETYPE_PEM));
ExpectNotNull(ssl = SSL_new(ctx));
ExpectNotNull(sk = SSL_get_ciphers(ssl));
ExpectIntGT(sk_SSL_CIPHER_num(sk), 0);

/* Pick the first cipher in ssl's list and round-trip via SSL_CIPHER_find. */
if (sk != NULL && sk_SSL_CIPHER_num(sk) > 0) {
const WOLFSSL_CIPHER* first = sk_SSL_CIPHER_value(sk, 0);
ExpectNotNull(first);
if (first != NULL) {
id[0] = first->cipherSuite0;
id[1] = first->cipherSuite;

ExpectNotNull(found = SSL_CIPHER_find(ssl, id));
if (found != NULL) {
ExpectIntEQ(found->cipherSuite0, id[0]);
ExpectIntEQ(found->cipherSuite, id[1]);
}
}
}

/* NULL arg handling. */
ExpectNull(SSL_CIPHER_find(NULL, id));
ExpectNull(SSL_CIPHER_find(ssl, NULL));

/* Suite not in ssl's cipher list. */
ExpectNull(SSL_CIPHER_find(ssl, bogus));

SSL_free(ssl);
SSL_CTX_free(ctx);
#endif
return EXPECT_RESULT();
}

static int test_wolfSSL_set1_curves_list(void)
{
EXPECT_DECLS;
Expand Down Expand Up @@ -40343,6 +40551,7 @@ TEST_CASE testCases[] = {
#endif
TEST_DECL(test_wolfSSL_configure_args),
TEST_DECL(test_wolfSSL_sk_SSL_CIPHER),
TEST_DECL(test_wolfSSL_SSL_CIPHER_find),
TEST_DECL(test_wolfSSL_set1_curves_list),
TEST_DECL(test_wolfSSL_curves_mismatch),
TEST_DECL(test_wolfSSL_set1_sigalgs_list),
Expand Down Expand Up @@ -40646,6 +40855,8 @@ TEST_CASE testCases[] = {
TEST_DECL(test_wolfSSL_CTX_load_verify_buffer_ex),
TEST_DECL(test_wolfSSL_CTX_load_verify_chain_buffer_format),
TEST_DECL(test_wolfSSL_CTX_add1_chain_cert),
TEST_DECL(test_wolfSSL_clear_chain_certs),
TEST_DECL(test_wolfSSL_clear_chain_certs_handshake),
TEST_DECL(test_wolfSSL_add_to_chain_overflow),
TEST_DECL(test_wolfSSL_CTX_use_certificate_chain_buffer_format),
TEST_DECL(test_wolfSSL_CTX_use_certificate_chain_file_format),
Expand Down
Loading
Loading