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
8 changes: 8 additions & 0 deletions src/ssl_api_cert.c
Original file line number Diff line number Diff line change
Expand Up @@ -1536,6 +1536,14 @@ void wolfSSL_CTX_set_cert_store(WOLFSSL_CTX* ctx, WOLFSSL_X509_STORE* str)
ctx->x509_store_pt = str;
/* Context has ownership and free it with context free. */
ctx->cm->x509_store_p = ctx->x509_store_pt;

#ifdef OPENSSL_EXTRA
/* Non-self-signed certs (intermediates) added via
* X509_STORE_add_cert only go into store->certs, not the
* CertManager. Push them into the CM now so that all
* verification paths can find them. */
X509StorePushCertsToCM(str);
#endif
}
}

Expand Down
63 changes: 63 additions & 0 deletions src/x509_str.c
Original file line number Diff line number Diff line change
Expand Up @@ -1561,6 +1561,69 @@ static int X509StoreAddCa(WOLFSSL_X509_STORE* store,
return result;
}

/* Push certificates from the store's X509 stacks (certs and trusted) into the
* CertManager, then free and NULL the stacks to signal that this store is now
* owned by an SSL_CTX.
*
* This is needed when an X509_STORE is attached to an SSL_CTX via
* SSL_CTX_set_cert_store: self-signed CAs are already in the CM (added by
* X509StoreAddCa during X509_STORE_add_cert), but non-self-signed intermediates
* are only in store->certs and must be explicitly added to the CM so that all
* verification paths (including CertManagerVerify) can find them. */
WOLFSSL_LOCAL int X509StorePushCertsToCM(WOLFSSL_X509_STORE* store)
{
int i;
int num;
int ret;
int anyFail = 0;
WOLFSSL_X509* x509;

WOLFSSL_ENTER("X509StorePushCertsToCM");

if (store == NULL || store->cm == NULL)
return WOLFSSL_SUCCESS;

/* Push non-self-signed intermediates from store->certs into the CM. */
if (store->certs != NULL) {
num = wolfSSL_sk_X509_num(store->certs);
for (i = 0; i < num; i++) {
x509 = wolfSSL_sk_X509_value(store->certs, i);
if (x509 != NULL) {
ret = X509StoreAddCa(store, x509, WOLFSSL_USER_CA);
if (ret != WOLFSSL_SUCCESS) {
WOLFSSL_MSG("X509StorePushCertsToCM: failed to add cert");
anyFail = 1;
}
}
}
/* Free and NULL to mark store as CTX-owned. Future add_cert calls
* will go directly to the CertManager. */
wolfSSL_sk_X509_pop_free(store->certs, NULL);
store->certs = NULL;
}

/* Push trusted certs too. Self-signed CAs are typically already in the CM
* (added during X509_STORE_add_cert), but AddCA handles duplicates. */
if (store->trusted != NULL) {
num = wolfSSL_sk_X509_num(store->trusted);
for (i = 0; i < num; i++) {
x509 = wolfSSL_sk_X509_value(store->trusted, i);
if (x509 != NULL) {
ret = X509StoreAddCa(store, x509, WOLFSSL_USER_CA);
if (ret != WOLFSSL_SUCCESS) {
WOLFSSL_MSG("X509StorePushCertsToCM: failed to add "
"trusted cert");
anyFail = 1;
}
}
}
wolfSSL_sk_X509_pop_free(store->trusted, NULL);
store->trusted = NULL;
}

return anyFail ? WOLFSSL_FATAL_ERROR : WOLFSSL_SUCCESS;
}

int wolfSSL_X509_STORE_add_cert(WOLFSSL_X509_STORE* store, WOLFSSL_X509* x509)
{
int result = WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR);
Expand Down
115 changes: 115 additions & 0 deletions tests/api/test_ossl_x509_str.c
Original file line number Diff line number Diff line change
Expand Up @@ -1794,3 +1794,118 @@ int test_X509_STORE_No_SSL_CTX(void)
return EXPECT_RESULT();
}

/* Test that SSL_CTX_set_cert_store propagates certificates (including
* non-self-signed intermediates) into the CertManager, and that certs
* added to the store after set_cert_store also reach the CertManager.
* Regression test for ZD 19760 / GitHub PR #8708.
*/
int test_wolfSSL_CTX_set_cert_store(void)
{
EXPECT_DECLS;
#if defined(OPENSSL_EXTRA) && !defined(NO_RSA) && !defined(NO_FILESYSTEM) && \
!defined(NO_WOLFSSL_CLIENT)
SSL_CTX* ctx = NULL;
X509_STORE* store = NULL;
X509* rootCa = NULL;
X509* intCa = NULL;
X509* int2Ca = NULL;
X509_STORE_CTX* storeCtx = NULL;
X509* svrCert = NULL;

const char caCert[] = "./certs/ca-cert.pem";
const char intCaCert[] = "./certs/intermediate/ca-int-cert.pem";
const char int2CaCert[] = "./certs/intermediate/ca-int2-cert.pem";
const char svrIntCert[] = "./certs/intermediate/server-int-cert.pem";

/* --- Part 1: Add certs to store BEFORE set_cert_store ---
* Non-self-signed intermediates should be pushed into the CertManager
* when set_cert_store is called. */
ExpectNotNull(store = X509_STORE_new());
ExpectNotNull(rootCa = wolfSSL_X509_load_certificate_file(caCert,
SSL_FILETYPE_PEM));
ExpectNotNull(intCa = wolfSSL_X509_load_certificate_file(intCaCert,
SSL_FILETYPE_PEM));
ExpectNotNull(int2Ca = wolfSSL_X509_load_certificate_file(int2CaCert,
SSL_FILETYPE_PEM));

ExpectIntEQ(X509_STORE_add_cert(store, rootCa), SSL_SUCCESS);
ExpectIntEQ(X509_STORE_add_cert(store, intCa), SSL_SUCCESS);
ExpectIntEQ(X509_STORE_add_cert(store, int2Ca), SSL_SUCCESS);

ExpectNotNull(ctx = SSL_CTX_new(TLS_client_method()));

/* This should push intermediates from store->certs into the CM */
SSL_CTX_set_cert_store(ctx, store);

/* After set_cert_store, store->certs and store->trusted should be NULLed
* to signal CTX ownership */
if (EXPECT_SUCCESS()) {
ExpectNull(store->certs);
ExpectNull(store->trusted);
}

/* Verify using CertManagerVerify - this only checks the CM, not the
* store's certs stack, so it proves the intermediates were pushed */
ExpectIntEQ(wolfSSL_CertManagerVerify(wolfSSL_CTX_GetCertManager(ctx),
svrIntCert, SSL_FILETYPE_PEM), WOLFSSL_SUCCESS);

/* Also verify using X509_verify_cert for completeness */
ExpectNotNull(svrCert = wolfSSL_X509_load_certificate_file(svrIntCert,
SSL_FILETYPE_PEM));
ExpectNotNull(storeCtx = X509_STORE_CTX_new());
if (EXPECT_SUCCESS()) {
ExpectIntEQ(X509_STORE_CTX_init(storeCtx,
SSL_CTX_get_cert_store(ctx), svrCert, NULL), SSL_SUCCESS);
ExpectIntEQ(X509_verify_cert(storeCtx), SSL_SUCCESS);
}

X509_STORE_CTX_free(storeCtx);
storeCtx = NULL;
X509_free(svrCert);
svrCert = NULL;
SSL_CTX_free(ctx);
ctx = NULL;
/* store is freed by SSL_CTX_free */
store = NULL;

X509_free(rootCa);
rootCa = NULL;
X509_free(intCa);
intCa = NULL;
X509_free(int2Ca);
int2Ca = NULL;

/* --- Part 2: Add certs to store AFTER set_cert_store ---
* When store->certs is NULL (CTX-owned), X509_STORE_add_cert should
* route non-self-signed certs directly to the CertManager. */
ExpectNotNull(store = X509_STORE_new());
ExpectNotNull(ctx = SSL_CTX_new(TLS_client_method()));

/* Attach empty store first */
SSL_CTX_set_cert_store(ctx, store);

/* Now add certs after ownership transfer */
ExpectNotNull(rootCa = wolfSSL_X509_load_certificate_file(caCert,
SSL_FILETYPE_PEM));
ExpectNotNull(intCa = wolfSSL_X509_load_certificate_file(intCaCert,
SSL_FILETYPE_PEM));
ExpectNotNull(int2Ca = wolfSSL_X509_load_certificate_file(int2CaCert,
SSL_FILETYPE_PEM));

ExpectIntEQ(X509_STORE_add_cert(store, rootCa), SSL_SUCCESS);
ExpectIntEQ(X509_STORE_add_cert(store, intCa), SSL_SUCCESS);
ExpectIntEQ(X509_STORE_add_cert(store, int2Ca), SSL_SUCCESS);

/* Verify that certs added after set_cert_store are in the CM */
ExpectIntEQ(wolfSSL_CertManagerVerify(wolfSSL_CTX_GetCertManager(ctx),
svrIntCert, SSL_FILETYPE_PEM), WOLFSSL_SUCCESS);

SSL_CTX_free(ctx);
/* store freed by SSL_CTX_free */
X509_free(rootCa);
X509_free(intCa);
X509_free(int2Ca);
#endif
return EXPECT_RESULT();
}

4 changes: 3 additions & 1 deletion tests/api/test_ossl_x509_str.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ int test_wolfSSL_X509_STORE_get1_certs(void);
int test_wolfSSL_X509_STORE_set_get_crl(void);
int test_wolfSSL_X509_CA_num(void);
int test_X509_STORE_No_SSL_CTX(void);
int test_wolfSSL_CTX_set_cert_store(void);

#define TEST_OSSL_X509_STORE_DECLS \
TEST_DECL_GROUP("ossl_x509_store", test_wolfSSL_X509_STORE_CTX_set_time), \
Expand All @@ -63,6 +64,7 @@ int test_X509_STORE_No_SSL_CTX(void);
TEST_DECL_GROUP("ossl_x509_store", test_wolfSSL_X509_STORE_get1_certs), \
TEST_DECL_GROUP("ossl_x509_store", test_wolfSSL_X509_STORE_set_get_crl), \
TEST_DECL_GROUP("ossl_x509_store", test_wolfSSL_X509_CA_num), \
TEST_DECL_GROUP("ossl_x509_store", test_X509_STORE_No_SSL_CTX)
TEST_DECL_GROUP("ossl_x509_store", test_X509_STORE_No_SSL_CTX), \
TEST_DECL_GROUP("ossl_x509_store", test_wolfSSL_CTX_set_cert_store)

#endif /* WOLFCRYPT_TEST_OSSL_X509_STR_H */
1 change: 1 addition & 0 deletions wolfssl/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -2734,6 +2734,7 @@ WOLFSSL_LOCAL void CleanupStoreCtxCallback(WOLFSSL_X509_STORE_CTX* store,
#endif /* !defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH) */
WOLFSSL_LOCAL int X509StoreLoadCertBuffer(WOLFSSL_X509_STORE *str,
byte *buf, word32 bufLen, int type);
WOLFSSL_LOCAL int X509StorePushCertsToCM(WOLFSSL_X509_STORE* store);
#endif /* !defined NO_CERTS */

/* wolfSSL Sock Addr */
Expand Down
Loading