From 7d4ee6aae7a464f8223a1e48d06c666a99dc5c08 Mon Sep 17 00:00:00 2001 From: Reda Chouk Date: Mon, 3 Aug 2026 16:34:46 +0200 Subject: [PATCH] Deep copy revoked cert entry extensions in DupCRL_Entry() when CRL_STATIC_REVOKED_LIST and OPENSSL_EXTRA are enabled, and add a regression test for the X509_CRL_dup path. --- src/crl.c | 31 +++++++++++++++++++++++++ tests/api/test_certman.c | 49 ++++++++++++++++++++++++++++++++++++++++ tests/api/test_certman.h | 2 ++ 3 files changed, 82 insertions(+) diff --git a/src/crl.c b/src/crl.c index 709af1c7210..ecd10aedbc8 100644 --- a/src/crl.c +++ b/src/crl.c @@ -1411,6 +1411,37 @@ static CRL_Entry* DupCRL_Entry(const CRL_Entry* ent, void* heap) CRL_Entry_free(dupl, heap); return NULL; } +#elif defined(OPENSSL_EXTRA) + { + int i; + + /* certs is an in-struct array living after verifyMutex, so the bulk + * copy above aliased every extensions pointer with the original's. + * Cleaning the whole array first. */ + for (i = 0; i < CRL_MAX_REVOKED_CERTS; i++) { + dupl->certs[i].extensions = NULL; + dupl->certs[i].extensionsSz = 0; + } + + /* Deep copy the entry extensions, as DupRevokedCertList() does for the + * linked-list build. */ + for (i = 0; i < ent->totalCerts; i++) { + if (ent->certs[i].extensions == NULL || + ent->certs[i].extensionsSz == 0) { + continue; + } + dupl->certs[i].extensions = (byte*)XMALLOC( + ent->certs[i].extensionsSz, heap, DYNAMIC_TYPE_REVOKED); + if (dupl->certs[i].extensions == NULL) { + WOLFSSL_MSG("Failed to allocate revoked cert extensions"); + CRL_Entry_free(dupl, heap); + return NULL; + } + XMEMCPY(dupl->certs[i].extensions, ent->certs[i].extensions, + ent->certs[i].extensionsSz); + dupl->certs[i].extensionsSz = ent->certs[i].extensionsSz; + } + } #endif #ifdef OPENSSL_EXTRA dupl->issuer = wolfSSL_X509_NAME_dup(ent->issuer); diff --git a/tests/api/test_certman.c b/tests/api/test_certman.c index a46a2028ea8..3c9674ed5b6 100644 --- a/tests/api/test_certman.c +++ b/tests/api/test_certman.c @@ -29,6 +29,7 @@ #endif #include +#include #include #include #include @@ -2602,6 +2603,54 @@ int test_wolfSSL_CRL_static_revoked_list(void) return EXPECT_RESULT(); } +int test_wolfSSL_CRL_static_revoked_list_dup(void) +{ + EXPECT_DECLS; +#if defined(CRL_STATIC_REVOKED_LIST) && defined(HAVE_CRL) && \ + defined(OPENSSL_EXTRA) && !defined(NO_RSA) && !defined(NO_CERTS) && \ + defined(WOLFSSL_PEM_TO_DER) && !defined(NO_FILESYSTEM) && \ + !defined(NO_STDIO_FILESYSTEM) + /* certs/crl/crl_reason.pem revokes serial 01 and carries a + * crlEntryExtensions (CRL Reason Code) for that entry. + * Under OPENSSL_EXTRA GetRevoked() heap-allocates + * RevokedCert.extensions to hold the raw DER of those extensions. */ + const char* crlReasonFile = "./certs/crl/crl_reason.pem"; + XFILE fp = XBADFILE; + WOLFSSL_X509_CRL* crl = NULL; + WOLFSSL_X509_CRL* dupl = NULL; + + ExpectTrue((fp = XFOPEN(crlReasonFile, "rb")) != XBADFILE); + ExpectNotNull(crl = wolfSSL_PEM_read_X509_CRL(fp, NULL, NULL, NULL)); + if (fp != XBADFILE) + XFCLOSE(fp); + + ExpectNotNull(crl != NULL ? crl->crlList : NULL); + ExpectIntGT((crl != NULL && crl->crlList != NULL) ? + crl->crlList->totalCerts : 0, 0); + ExpectNotNull((crl != NULL && crl->crlList != NULL) ? + crl->crlList->certs[0].extensions : NULL); + + ExpectNotNull(dupl = wolfSSL_X509_CRL_dup(crl)); + + /* Every duplicated revoked cert must own its own extensions buffer. This + * fails when the bulk copy aliased them. */ + if (crl != NULL && dupl != NULL && crl->crlList != NULL && + dupl->crlList != NULL) { + int i; + for (i = 0; i < crl->crlList->totalCerts; i++) { + if (crl->crlList->certs[i].extensions != NULL) { + ExpectPtrNE(dupl->crlList->certs[i].extensions, + crl->crlList->certs[i].extensions); + } + } + } + + wolfSSL_X509_CRL_free(dupl); + wolfSSL_X509_CRL_free(crl); +#endif + return EXPECT_RESULT(); +} + int test_wolfSSL_CRL_duplicate_extensions(void) { EXPECT_DECLS; diff --git a/tests/api/test_certman.h b/tests/api/test_certman.h index 6575a9e0b0a..5ce8020152b 100644 --- a/tests/api/test_certman.h +++ b/tests/api/test_certman.h @@ -44,6 +44,7 @@ int test_wolfSSL_X509_check_host_URI_SAN_not_DNS_match(void); int test_wolfSSL_CertManagerCRL(void); int test_wolfSSL_CRL_reason_extensions_cleanup(void); int test_wolfSSL_CRL_static_revoked_list(void); +int test_wolfSSL_CRL_static_revoked_list_dup(void); int test_wolfSSL_CRL_duplicate_extensions(void); int test_wolfSSL_CRL_critical_idp(void); int test_wolfSSL_CRL_unknown_critical_ext(void); @@ -82,6 +83,7 @@ int test_wolfSSL_CertManagerNameConstraint_skid_disambiguates(void); TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerCRL), \ TEST_DECL_GROUP("certman", test_wolfSSL_CRL_reason_extensions_cleanup), \ TEST_DECL_GROUP("certman", test_wolfSSL_CRL_static_revoked_list), \ + TEST_DECL_GROUP("certman", test_wolfSSL_CRL_static_revoked_list_dup), \ TEST_DECL_GROUP("certman", test_wolfSSL_CRL_duplicate_extensions), \ TEST_DECL_GROUP("certman", test_wolfSSL_CRL_critical_idp), \ TEST_DECL_GROUP("certman", test_wolfSSL_CRL_unknown_critical_ext), \