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
5 changes: 3 additions & 2 deletions src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -26238,7 +26238,8 @@ int SendCertificateStatus(WOLFSSL* ssl)
}

if (chain && chain->buffer) {
while (ret == 0 && idx + OPAQUE24_LEN < chain->length) {
while (ret == 0 &&
idx + OPAQUE24_LEN < chain->length) {
c24to32(chain->buffer + idx, &der.length);
idx += OPAQUE24_LEN;

Expand Down Expand Up @@ -26279,7 +26280,7 @@ int SendCertificateStatus(WOLFSSL* ssl)
WC_FREE_VAR_EX(cert, ssl->heap, DYNAMIC_TYPE_DCERT);
}
else {
while (ret == 0 &&
while (ret == 0 && i < MAX_CHAIN_DEPTH &&
NULL != (request = ssl->ctx->chainOcspRequest[i])) {
if ((i + 1) >= MAX_CERT_EXTENSIONS) {
ret = MAX_CERT_EXTENSIONS_ERR;
Expand Down
6 changes: 3 additions & 3 deletions src/ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -14139,7 +14139,7 @@ int wolfSSL_get_chain_count(WOLFSSL_X509_CHAIN* chain)
int wolfSSL_get_chain_length(WOLFSSL_X509_CHAIN* chain, int idx)
{
WOLFSSL_ENTER("wolfSSL_get_chain_length");
if (chain)
if (chain && idx >= 0 && idx < chain->count)
return chain->certs[idx].length;

return 0;
Expand All @@ -14150,7 +14150,7 @@ int wolfSSL_get_chain_length(WOLFSSL_X509_CHAIN* chain, int idx)
byte* wolfSSL_get_chain_cert(WOLFSSL_X509_CHAIN* chain, int idx)
{
WOLFSSL_ENTER("wolfSSL_get_chain_cert");
if (chain)
if (chain && idx >= 0 && idx < chain->count)
return chain->certs[idx].buffer;

return 0;
Expand All @@ -14165,7 +14165,7 @@ WOLFSSL_X509* wolfSSL_get_chain_X509(WOLFSSL_X509_CHAIN* chain, int idx)
WC_DECLARE_VAR(cert, DecodedCert, 1, 0);

WOLFSSL_ENTER("wolfSSL_get_chain_X509");
if (chain != NULL && idx < MAX_CHAIN_DEPTH) {
if (chain != NULL && idx >= 0 && idx < chain->count) {
#ifdef WOLFSSL_SMALL_STACK
cert = (DecodedCert*)XMALLOC(sizeof(DecodedCert), NULL,
DYNAMIC_TYPE_DCERT);
Expand Down
7 changes: 7 additions & 0 deletions src/ssl_load.c
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,13 @@ static int ProcessUserChain(WOLFSSL_CTX* ctx, WOLFSSL* ssl,
ret = DataToDerBuffer(buff + consumed, (word32)(sz - consumed),
format, type, info, heap, &part, NULL);
if (ret == 0) {
/* Enforce maximum chain depth once a real cert is parsed. */
if (cnt >= MAX_CHAIN_DEPTH) {
WOLFSSL_MSG("Chain depth limit reached");
FreeDer(&part);
ret = MAX_CHAIN_ERROR;
break;
}
/* Process the user certificate. */
ret = ProcessUserCert(ctx->cm, &part, type, verify,
chain.buffer, &idx, (word32)maxSz);
Expand Down
8 changes: 7 additions & 1 deletion src/tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -2826,6 +2826,9 @@ int TLSX_SNI_GetFromBuffer(const byte* clientHello, word32 helloSz,
ato16(clientHello + offset, &listLen);
offset += OPAQUE16_LEN;

if (listLen != extLen - OPAQUE16_LEN)
return BUFFER_ERROR;

if (helloSz < offset + listLen)
return BUFFER_ERROR;

Expand All @@ -2836,6 +2839,9 @@ int TLSX_SNI_GetFromBuffer(const byte* clientHello, word32 helloSz,
ato16(clientHello + offset, &sniLen);
offset += OPAQUE16_LEN;

if (sniLen > listLen - (ENUM_LEN + OPAQUE16_LEN))
return BUFFER_ERROR;

if (helloSz < offset + sniLen)
return BUFFER_ERROR;

Expand Down Expand Up @@ -3393,7 +3399,7 @@ static void TLSX_CSR_Free(CertificateStatusRequest* csr, void* heap)

switch (csr->status_type) {
case WOLFSSL_CSR_OCSP:
for (i = 0; i <= csr->requests; i++) {
for (i = 0; i < csr->requests; i++) {
FreeOcspRequest(&csr->request.ocsp[i]);
}
break;
Expand Down
4 changes: 4 additions & 0 deletions src/tls13.c
Original file line number Diff line number Diff line change
Expand Up @@ -9520,6 +9520,10 @@ static int SendTls13Certificate(WOLFSSL* ssl)
}
/* Certificate Data */
certSz = ssl->buffers.certificate->length;
if (ssl->buffers.certChainCnt > MAX_CHAIN_DEPTH) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚪ [Info] SendTls13Certificate depth guard is now defense-in-depth only and its direct test path was removed

With the new ProcessUserChain limit, an oversized chain can no longer be loaded (it fails with MAX_CHAIN_ERROR at load time), so the new ssl->buffers.certChainCnt > MAX_CHAIN_DEPTH guard inside SendTls13Certificate is effectively unreachable via the normal load path -- it is a reasonable belt-and-suspenders check, but untested. The PR simultaneously deletes test_dtls13_oversized_cert_chain, which was the only test that drove an oversized chain through the actual TLS1.3 certificate-send path. The deletion itself is justified (that test expected WOLFSSL_SUCCESS on load, which is no longer possible), and the test_dtls13.h macro list trailing-comma update is correct. Net effect is reduced direct coverage of the send-side guard.

Fix: Optional: keep the guard (cheap and defensive) but note in a comment that it is unreachable under normal loading, or add a unit test that sets ssl->buffers.certChainCnt directly to exercise the MAX_CHAIN_ERROR return.

WOLFSSL_MSG("Certificate chain count exceeds maximum depth");
return MAX_CHAIN_ERROR;
}
/* Cert Req Ctx Len | Cert Req Ctx | Cert List Len | Cert Data Len */
headerSz = OPAQUE8_LEN + certReqCtxLen + CERT_HEADER_SZ +
CERT_HEADER_SZ;
Expand Down
104 changes: 104 additions & 0 deletions tests/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -3831,6 +3831,108 @@ static int test_wolfSSL_CTX_use_certificate_chain_buffer_format(void)
return EXPECT_RESULT();
}

/* wolfSSL_get_chain_{length,cert,X509} must reject out-of-range idx. */
static int test_wolfSSL_get_chain_idx_bounds(void)
{
EXPECT_DECLS;
#if defined(SESSION_CERTS) && \
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES)
struct test_memio_ctx test_ctx;
WOLFSSL_CTX* ctx_c = NULL;
WOLFSSL_CTX* ctx_s = NULL;
WOLFSSL* ssl_c = NULL;
WOLFSSL* ssl_s = NULL;
WOLFSSL_X509_CHAIN* chain = NULL;
int count = 0;

XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfTLS_client_method, wolfTLS_server_method), 0);
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);

ExpectNotNull(chain = wolfSSL_get_peer_chain(ssl_c));
ExpectIntGT(count = wolfSSL_get_chain_count(chain), 0);

ExpectIntEQ(wolfSSL_get_chain_length(chain, -1), 0);
ExpectIntEQ(wolfSSL_get_chain_length(chain, count), 0);
ExpectIntEQ(wolfSSL_get_chain_length(chain, MAX_CHAIN_DEPTH), 0);
ExpectNull(wolfSSL_get_chain_cert(chain, -1));
ExpectNull(wolfSSL_get_chain_cert(chain, count));
ExpectNull(wolfSSL_get_chain_cert(chain, MAX_CHAIN_DEPTH));
#ifdef OPENSSL_EXTRA
{
WOLFSSL_X509* x = NULL;
ExpectNull(x = wolfSSL_get_chain_X509(chain, -1));
if (x != NULL) { wolfSSL_X509_free(x); x = NULL; }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] Max-depth load test overshoots the limit and omits the exact-boundary success case

The test sets nCerts = MAX_CHAIN_DEPTH + 1 copies of svrCertFile, but server-cert.pem contains 2 certificates, so the buffer actually holds ~2*(MAX_CHAIN_DEPTH+1) certs. It therefore only proves that a grossly-oversized chain is rejected and does not pin the boundary. It is missing the complementary positive case: a chain of exactly MAX_CHAIN_DEPTH certificates must still load successfully. That gap is exactly what would let the placement issue in ProcessUserChain (separate finding) slip through. The const int nCerts = MAX_CHAIN_DEPTH + 1; naming/comment is also misleading given each file is multi-cert.

Fix: Add a positive boundary test (exactly MAX_CHAIN_DEPTH certs -> success) and clarify the per-file cert count in the comment.

ExpectNull(x = wolfSSL_get_chain_X509(chain, count));
if (x != NULL) { wolfSSL_X509_free(x); x = NULL; }
ExpectNull(x = wolfSSL_get_chain_X509(chain, MAX_CHAIN_DEPTH));
if (x != NULL) { wolfSSL_X509_free(x); x = NULL; }
}
#endif

wolfSSL_free(ssl_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_c);
wolfSSL_CTX_free(ctx_s);
#endif
return EXPECT_RESULT();
}

/* Chain-depth boundary: exactly MAX_CHAIN_DEPTH chain certs (plus trailing data
* that forces one more parse pass) must load; one more cert must be rejected.
* cliCertFile is single-cert, so copy 0 is the leaf and N copies => N-1 chain. */
static int test_wolfSSL_CTX_use_certificate_chain_buffer_max_depth(void)
{
EXPECT_DECLS;
#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && !defined(NO_TLS) && \
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_RSA) && \
defined(WOLFSSL_PEM_TO_DER)
WOLFSSL_CTX* ctx = NULL;
unsigned char* one = NULL;
unsigned char* buf = NULL;
size_t oneLen = 0;
const char* tail = "# trailing comment\n";
size_t tailLen = XSTRLEN(tail);
/* +1 copy for the leaf yields exactly MAX_CHAIN_DEPTH chain certs. */
const int atMax = MAX_CHAIN_DEPTH + 1;
int i;

ExpectIntEQ(load_file(cliCertFile, &one, &oneLen), 0);

/* Exactly MAX_CHAIN_DEPTH chain certs + trailing data: loads. */
ExpectNotNull(buf = (unsigned char*)XMALLOC(
oneLen * (size_t)atMax + tailLen, NULL, DYNAMIC_TYPE_TMP_BUFFER));
for (i = 0; EXPECT_SUCCESS() && i < atMax; i++)
XMEMCPY(buf + (size_t)i * oneLen, one, oneLen);
if (buf != NULL)
XMEMCPY(buf + (size_t)atMax * oneLen, tail, tailLen);
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()));
ExpectIntEQ(wolfSSL_CTX_use_certificate_chain_buffer(ctx, buf,
(long)(oneLen * (size_t)atMax + tailLen)), WOLFSSL_SUCCESS);
wolfSSL_CTX_free(ctx);
ctx = NULL;
if (buf != NULL)
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
buf = NULL;

/* One more chain cert: rejected. */
ExpectNotNull(buf = (unsigned char*)XMALLOC(oneLen * (size_t)(atMax + 1),
NULL, DYNAMIC_TYPE_TMP_BUFFER));
for (i = 0; EXPECT_SUCCESS() && i < atMax + 1; i++)
XMEMCPY(buf + (size_t)i * oneLen, one, oneLen);
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()));
ExpectIntEQ(wolfSSL_CTX_use_certificate_chain_buffer(ctx, buf,
(long)(oneLen * (size_t)(atMax + 1))), WC_NO_ERR_TRACE(MAX_CHAIN_ERROR));
wolfSSL_CTX_free(ctx);
if (buf != NULL)
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (one != NULL)
XFREE(one, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
return EXPECT_RESULT();
}

static int test_wolfSSL_CTX_use_certificate_chain_file_format(void)
{
EXPECT_DECLS;
Expand Down Expand Up @@ -34777,6 +34879,8 @@ TEST_CASE testCases[] = {
TEST_DECL(test_wolfSSL_CTX_add1_chain_cert),
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_buffer_max_depth),
TEST_DECL(test_wolfSSL_get_chain_idx_bounds),
TEST_DECL(test_wolfSSL_CTX_use_certificate_chain_file_format),
TEST_DECL(test_wolfSSL_use_certificate_chain_file),
TEST_DECL(test_wolfSSL_CTX_trust_peer_cert),
Expand Down
94 changes: 0 additions & 94 deletions tests/api/test_dtls13.c
Original file line number Diff line number Diff line change
Expand Up @@ -1779,97 +1779,3 @@ int test_dtls13_5_9_0_compat(void)
#endif
return EXPECT_RESULT();
}

/*-- oversized_cert_chain (test_dtls.c lines 3174,3265) ---*/
int test_dtls13_oversized_cert_chain(void)
{
EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13) \
&& !defined(NO_FILESYSTEM) && !defined(NO_RSA)
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
struct test_memio_ctx test_ctx;
XFILE f = XBADFILE;
long sz = 0;
byte *cert = NULL;
byte *chain = NULL;
int copies, off, i;

XMEMSET(&test_ctx, 0, sizeof(test_ctx));

/* Read server cert */
f = XFOPEN(svrCertFile, "rb");
ExpectTrue(f != XBADFILE);
if (EXPECT_SUCCESS()) {
(void)XFSEEK(f, 0, XSEEK_END);
sz = XFTELL(f);
(void)XFSEEK(f, 0, XSEEK_SET);
}
ExpectTrue(sz > 0);
cert = (byte*)XMALLOC((size_t)(sz + 1), NULL, DYNAMIC_TYPE_TMP_BUFFER);
ExpectNotNull(cert);
if (EXPECT_SUCCESS())
ExpectIntEQ((int)XFREAD(cert, 1, (size_t)sz, f), (int)sz);
if (f != XBADFILE)
XFCLOSE(f);

/* Build an oversized chain by duplicating the cert */
copies = EXPECT_SUCCESS() ? (int)(70000 / sz) + 2 : 0;
chain = (byte*)XMALLOC((size_t)(sz * copies + 1), NULL,
DYNAMIC_TYPE_TMP_BUFFER);
ExpectNotNull(chain);
off = 0;
if (EXPECT_SUCCESS()) {
for (i = 0; i < copies; i++) {
XMEMCPY(chain + off, cert, (size_t)sz);
off += (int)sz;
}
}

/* Server context: load the oversized chain */
ExpectNotNull(ctx_s = wolfSSL_CTX_new(wolfDTLSv1_3_server_method()));
ExpectIntEQ(wolfSSL_CTX_use_certificate_chain_buffer(ctx_s,
chain, (long)off), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_s, svrKeyFile,
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
if (EXPECT_SUCCESS()) {
wolfSSL_SetIORecv(ctx_s, test_memio_read_cb);
wolfSSL_SetIOSend(ctx_s, test_memio_write_cb);
}

/* Client context: no verification (chain certs are duplicates) */
ExpectNotNull(ctx_c = wolfSSL_CTX_new(wolfDTLSv1_3_client_method()));
if (EXPECT_SUCCESS()) {
wolfSSL_CTX_set_verify(ctx_c, WOLFSSL_VERIFY_NONE, NULL);
wolfSSL_SetIORecv(ctx_c, test_memio_read_cb);
wolfSSL_SetIOSend(ctx_c, test_memio_write_cb);
}

ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
if (EXPECT_SUCCESS()) {
wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
}

ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
if (EXPECT_SUCCESS()) {
wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
}

/* Handshake must not crash. If SendTls13Certificate mishandles the
* oversized chain this will trigger a wild pointer dereference or stack
* overflow resulting with the test failing.
* The correct behaviour either returns BUFFER_E or succeeds
* if the build config truncated the chain during loading. */
(void)test_memio_do_handshake(ssl_c, ssl_s, 10, NULL);

wolfSSL_free(ssl_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_c);
wolfSSL_CTX_free(ctx_s);
XFREE(cert, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(chain, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
return EXPECT_RESULT();
}
4 changes: 1 addition & 3 deletions tests/api/test_dtls13.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ int test_dtls_srtp(void);
int test_dtls13_min_rtx_interval(void);
int test_dtls13_no_session_id_echo(void);
int test_dtls13_5_9_0_compat(void);
int test_dtls13_oversized_cert_chain(void);

#define TEST_DTLS13_DECLS \
TEST_DECL_GROUP("dtls13", test_dtls13_bad_epoch_ch), \
Expand All @@ -75,7 +74,6 @@ int test_dtls13_oversized_cert_chain(void);
TEST_DECL_GROUP("dtls13", test_dtls_srtp), \
TEST_DECL_GROUP("dtls13", test_dtls13_min_rtx_interval), \
TEST_DECL_GROUP("dtls13", test_dtls13_no_session_id_echo), \
TEST_DECL_GROUP("dtls13", test_dtls13_5_9_0_compat), \
TEST_DECL_GROUP("dtls13", test_dtls13_oversized_cert_chain)
TEST_DECL_GROUP("dtls13", test_dtls13_5_9_0_compat)

#endif /* TESTS_API_DTLS13_H */
60 changes: 60 additions & 0 deletions tests/api/test_pkcs7.c
Original file line number Diff line number Diff line change
Expand Up @@ -2873,6 +2873,66 @@ int test_wc_PKCS7_DecodeEnvelopedData_forgedRecipientSetLen(void)
} /* END test_wc_PKCS7_DecodeEnvelopedData_forgedRecipientSetLen() */


/* Decoding an AuthEnvelopedData blob whose encryptedContent or authTag
* is truncated must return BUFFER_E rather than reading past pkiMsg. */
int test_wc_PKCS7_DecodeAuthEnvelopedData_truncated(void)
{
EXPECT_DECLS;
#if defined(HAVE_PKCS7) && defined(HAVE_AESGCM) && !defined(NO_RSA) && \
!defined(NO_AES) && defined(WOLFSSL_AES_128) && defined(NO_PKCS7_STREAM)
PKCS7* pkcs7 = NULL;
byte enveloped[2048];
byte decoded[256];
byte data[] = "truncated authEnvelopedData test";
int encSz = 0;

ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId));
ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, (byte*)client_cert_der_2048,
sizeof_client_cert_der_2048), 0);
if (pkcs7 != NULL) {
pkcs7->content = data;
pkcs7->contentSz = (word32)sizeof(data);
pkcs7->contentOID = DATA;
pkcs7->encryptOID = AES128GCMb;
}
/* >32 so the encSz-32 / encSz-1 truncations below can't underflow */
ExpectIntGT(encSz = wc_PKCS7_EncodeAuthEnvelopedData(pkcs7, enveloped,
sizeof(enveloped)), 32);
wc_PKCS7_Free(pkcs7);
pkcs7 = NULL;

/* Truncate inside encryptedContent (encryptedContentSz check). */
ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId));
ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, (byte*)client_cert_der_2048,
sizeof_client_cert_der_2048), 0);
if (pkcs7 != NULL) {
pkcs7->privateKey = (byte*)client_key_der_2048;
pkcs7->privateKeySz = sizeof_client_key_der_2048;
}
ExpectIntEQ(wc_PKCS7_DecodeAuthEnvelopedData(pkcs7, enveloped,
(word32)encSz - 32, decoded, sizeof(decoded)),
WC_NO_ERR_TRACE(BUFFER_E));
wc_PKCS7_Free(pkcs7);
pkcs7 = NULL;
Comment thread
ColtonWilley marked this conversation as resolved.
Comment thread
ColtonWilley marked this conversation as resolved.

/* Truncate one byte off the auth tag (authTagSz check). */
ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId));
ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, (byte*)client_cert_der_2048,
sizeof_client_cert_der_2048), 0);
if (pkcs7 != NULL) {
pkcs7->privateKey = (byte*)client_key_der_2048;
pkcs7->privateKeySz = sizeof_client_key_der_2048;
}
ExpectIntEQ(wc_PKCS7_DecodeAuthEnvelopedData(pkcs7, enveloped,
(word32)encSz - 1, decoded, sizeof(decoded)),
WC_NO_ERR_TRACE(BUFFER_E));

wc_PKCS7_Free(pkcs7);
#endif
return EXPECT_RESULT();
} /* END test_wc_PKCS7_DecodeAuthEnvelopedData_truncated() */


/*
* Testing wc_PKCS7_DecodeEnvelopedData with streaming
*/
Expand Down
Loading
Loading