Fix wolfSSL_sk_X509_OBJECT_deep_copy to check bounds

This commit is contained in:
Eric Blankenhorn
2026-04-09 14:41:36 -05:00
parent 178e10e42a
commit 33310010a9
3 changed files with 209 additions and 7 deletions
+21 -6
View File
@@ -11703,23 +11703,38 @@ WOLF_STACK_OF(WOLFSSL_X509_OBJECT)* wolfSSL_sk_X509_OBJECT_deep_copy(
}
if (ret == WOLFSSL_SUCCESS) {
#if defined(OPENSSL_ALL)
int idx;
#endif
cert->version = req->version;
cert->isCA = req->isCa;
cert->basicConstSet = req->basicConstSet;
#ifdef WOLFSSL_CERT_EXT
if (req->subjKeyIdSz != 0) {
XMEMCPY(cert->skid, req->subjKeyId, req->subjKeyIdSz);
cert->skidSz = (int)req->subjKeyIdSz;
if (req->subjKeyIdSz > CTC_MAX_SKID_SIZE) {
WOLFSSL_MSG("Subject Key ID too large");
WOLFSSL_ERROR_VERBOSE(BUFFER_E);
ret = WOLFSSL_FAILURE;
}
else if (req->subjKeyId == NULL) {
WOLFSSL_MSG("Subject Key ID missing");
WOLFSSL_ERROR_VERBOSE(BAD_FUNC_ARG);
ret = WOLFSSL_FAILURE;
}
else {
XMEMCPY(cert->skid, req->subjKeyId,
req->subjKeyIdSz);
cert->skidSz = (int)req->subjKeyIdSz;
}
}
if (req->keyUsageSet)
cert->keyUsage = req->keyUsage;
cert->extKeyUsage = req->extKeyUsage;
#endif
}
if (ret == WOLFSSL_SUCCESS) {
#if defined(OPENSSL_ALL)
int idx;
#endif
XMEMCPY(cert->challengePw, req->challengePw, CTC_NAME_SIZE);
cert->challengePwPrintableString = req->challengePw[0] != 0;
+183
View File
@@ -878,3 +878,186 @@ int test_x509_CertFromX509_akid_overflow(void)
#endif
return EXPECT_RESULT();
}
/* Test that ReqCertFromX509() rejects a CSR with an oversized
* SubjectKeyIdentifier (> CTC_MAX_SKID_SIZE = 32 bytes). Before the fix
* this would cause a heap buffer overflow into cert->skid[32]. */
int test_x509_ReqCertFromX509_skid_overflow(void)
{
EXPECT_DECLS;
#if defined(WOLFSSL_CERT_REQ) && defined(WOLFSSL_CERT_GEN) && \
defined(WOLFSSL_CERT_EXT) && !defined(NO_BIO) && \
(defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)) && \
defined(HAVE_ECC)
/* Minimal DER-encoded CSR (PKCS#10) containing a SubjectKeyIdentifier
* extension with a 64-byte value -- double the 32-byte CTC_MAX_SKID_SIZE
* destination buffer.
*
* Structure:
* SEQUENCE {
* CertificationRequestInfo SEQUENCE {
* version INTEGER 0
* subject: CN=Test
* subjectPKInfo: EC P-256 (generator point)
* attributes [0] {
* SEQUENCE {
* OID 1.2.840.113549.1.9.14 (extensionRequest)
* SET { SEQUENCE { -- Extensions
* SEQUENCE { -- Extension
* OID 2.5.29.14 (subjectKeyIdentifier)
* OCTET STRING { OCTET STRING (64 bytes of 0x41) }
* }
* }}
* }
* }
* }
* signatureAlgorithm: ecdsa-with-SHA256
* signatureValue: dummy
* }
*/
static const unsigned char crafted_csr_der[] = {
0x30, 0x81, 0xE7, 0x30, 0x81, 0xCD, 0x02, 0x01,
0x00, 0x30, 0x0F, 0x31, 0x0D, 0x30, 0x0B, 0x06,
0x03, 0x55, 0x04, 0x03, 0x0C, 0x04, 0x54, 0x65,
0x73, 0x74, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07,
0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06,
0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01,
0x07, 0x03, 0x42, 0x00, 0x04, 0x6B, 0x17, 0xD1,
0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6,
0xE5, 0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D,
0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39,
0x45, 0xD8, 0x98, 0xC2, 0x96, 0x4F, 0xE3, 0x42,
0xE2, 0xFE, 0x1A, 0x7F, 0x9B, 0x8E, 0xE7, 0xEB,
0x4A, 0x7C, 0x0F, 0x9E, 0x16, 0x2B, 0xCE, 0x33,
0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40,
0x68, 0x37, 0xBF, 0x51, 0xF5, 0xA0, 0x5C, 0x30,
0x5A, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7,
0x0D, 0x01, 0x09, 0x0E, 0x31, 0x4D, 0x30, 0x4B,
0x30, 0x49, 0x06, 0x03, 0x55, 0x1D, 0x0E, 0x04,
0x42, 0x04, 0x40,
/* 64 bytes of 0x41 -- oversized SKID value */
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
/* end of SKID payload */
0x30, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE,
0x3D, 0x04, 0x03, 0x02, 0x03, 0x09, 0x00, 0x30,
0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01
};
WOLFSSL_X509* req = NULL;
WOLFSSL_BIO* bio = NULL;
/* Step 1: Parse the crafted CSR -- this should succeed (the parser
* dynamically allocates subjKeyId to the parsed size). */
req = wolfSSL_X509_REQ_d2i(NULL, crafted_csr_der,
(int)sizeof(crafted_csr_der));
ExpectNotNull(req);
/* Step 2: Attempt to re-encode. Before the fix, this triggered a
* heap buffer overflow in ReqCertFromX509() writing 64 bytes into
* cert->skid[32]. With the fix, it must return failure. */
bio = wolfSSL_BIO_new(wolfSSL_BIO_s_mem());
ExpectNotNull(bio);
ExpectIntNE(wolfSSL_i2d_X509_REQ_bio(bio, req), WOLFSSL_SUCCESS);
wolfSSL_BIO_free(bio);
wolfSSL_X509_free(req);
#endif
return EXPECT_RESULT();
}
/* Positive / boundary companion to test_x509_ReqCertFromX509_skid_overflow.
* Verifies that a CSR with a SubjectKeyIdentifier of exactly
* CTC_MAX_SKID_SIZE (32) bytes -- the boundary of the bounds check in
* ReqCertFromX509() -- is accepted and that the SKID round-trips with the
* correct length and bytes through the sign / re-encode / re-parse path.
* This kills boundary mutations (">" -> ">=") and copy-size mutations on
* the XMEMCPY into cert->skid that the negative test alone cannot catch. */
int test_x509_ReqCertFromX509_skid_boundary(void)
{
EXPECT_DECLS;
#if defined(WOLFSSL_CERT_REQ) && defined(WOLFSSL_CERT_GEN) && \
defined(WOLFSSL_CERT_EXT) && !defined(NO_BIO) && \
(defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)) && \
defined(HAVE_ECC) && defined(USE_CERT_BUFFERS_256)
WOLFSSL_EVP_PKEY* priv = NULL;
WOLFSSL_EVP_PKEY* pub = NULL;
WOLFSSL_X509* req = NULL;
WOLFSSL_X509* parsed = NULL;
WOLFSSL_X509_NAME* name = NULL;
unsigned char* der = NULL;
int derSz;
const unsigned char* ecPriv = ecc_clikey_der_256;
const unsigned char* ecPub = ecc_clikeypub_der_256;
unsigned char expected_skid[CTC_MAX_SKID_SIZE];
XMEMSET(expected_skid, 0x41, sizeof(expected_skid));
/* Load a real ECC keypair so that the CSR can actually be signed
* (ReqCertFromX509() is invoked from the signing path). */
ExpectNotNull(priv = wolfSSL_d2i_PrivateKey(EVP_PKEY_EC, NULL, &ecPriv,
(long)sizeof_ecc_clikey_der_256));
ExpectNotNull(pub = wolfSSL_d2i_PUBKEY(NULL, &ecPub,
(long)sizeof_ecc_clikeypub_der_256));
ExpectNotNull(req = wolfSSL_X509_REQ_new());
ExpectNotNull(name = wolfSSL_X509_NAME_new());
ExpectIntEQ(wolfSSL_X509_NAME_add_entry_by_txt(name, "commonName",
MBSTRING_UTF8, (const byte*)"Test", 4, -1, 0),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_X509_REQ_set_subject_name(req, name), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_X509_REQ_set_pubkey(req, pub), WOLFSSL_SUCCESS);
/* Inject exactly CTC_MAX_SKID_SIZE bytes of SKID directly on the req
* (tests/api/test_x509.c already includes <wolfssl/internal.h>). This
* is the boundary value of the bounds check in ReqCertFromX509(). */
if (EXPECT_SUCCESS() && req != NULL) {
req->subjKeyId = (byte*)XMALLOC(sizeof(expected_skid), NULL,
DYNAMIC_TYPE_X509_EXT);
ExpectNotNull(req->subjKeyId);
if (req->subjKeyId != NULL) {
XMEMCPY(req->subjKeyId, expected_skid, sizeof(expected_skid));
req->subjKeyIdSz = (word32)sizeof(expected_skid);
}
}
/* Signing invokes wolfssl_x509_make_der() -> ReqCertFromX509(), which
* executes the bounds check and the XMEMCPY into cert->skid. A
* ">=" boundary mutation would make this step fail for a 32-byte
* SKID. */
ExpectIntEQ(wolfSSL_X509_REQ_sign(req, priv, wolfSSL_EVP_sha256()),
WOLFSSL_SUCCESS);
/* Re-encode the now-signed CSR and parse it back to verify the SKID
* round-trips with the correct length and bytes. This verifies the
* output of the XMEMCPY in ReqCertFromX509(). */
ExpectIntGT((derSz = wolfSSL_i2d_X509_REQ(req, &der)), 0);
ExpectNotNull(der);
ExpectNotNull(parsed = wolfSSL_X509_REQ_d2i(NULL, der, derSz));
if (parsed != NULL) {
ExpectIntEQ((int)parsed->subjKeyIdSz, CTC_MAX_SKID_SIZE);
ExpectNotNull(parsed->subjKeyId);
if (parsed->subjKeyId != NULL) {
ExpectIntEQ(XMEMCMP(parsed->subjKeyId, expected_skid,
CTC_MAX_SKID_SIZE), 0);
}
}
wolfSSL_X509_free(parsed);
XFREE(der, NULL, DYNAMIC_TYPE_OPENSSL);
wolfSSL_X509_NAME_free(name);
wolfSSL_X509_free(req);
wolfSSL_EVP_PKEY_free(pub);
wolfSSL_EVP_PKEY_free(priv);
#endif
return EXPECT_RESULT();
}
+5 -1
View File
@@ -28,6 +28,8 @@ int test_x509_set_serialNumber(void);
int test_x509_verify_cert_hostname_check(void);
int test_x509_time_field_overread_via_tls(void);
int test_x509_CertFromX509_akid_overflow(void);
int test_x509_ReqCertFromX509_skid_overflow(void);
int test_x509_ReqCertFromX509_skid_boundary(void);
#define TEST_X509_DECLS \
TEST_DECL_GROUP("x509", test_x509_rfc2818_verification_callback), \
@@ -35,6 +37,8 @@ int test_x509_CertFromX509_akid_overflow(void);
TEST_DECL_GROUP("x509", test_x509_set_serialNumber), \
TEST_DECL_GROUP("x509", test_x509_verify_cert_hostname_check), \
TEST_DECL_GROUP("x509", test_x509_time_field_overread_via_tls), \
TEST_DECL_GROUP("x509", test_x509_CertFromX509_akid_overflow)
TEST_DECL_GROUP("x509", test_x509_CertFromX509_akid_overflow), \
TEST_DECL_GROUP("x509", test_x509_ReqCertFromX509_skid_overflow), \
TEST_DECL_GROUP("x509", test_x509_ReqCertFromX509_skid_boundary)
#endif /* WOLFCRYPT_TEST_X509_H */