From 94b36c9092a30a81320dcdf021fd54e4773125da Mon Sep 17 00:00:00 2001 From: Colton Willey Date: Mon, 13 Apr 2026 13:14:26 -0700 Subject: [PATCH] Harden chain depth bounds and parser input validation Enforce MAX_CHAIN_DEPTH limits in OCSP chain processing (SendCertificateStatus, ProcessChainOCSPRequest), certificate loading (ProcessUserChain), and TLS 1.3 certificate sending (SendTls13Certificate). Add idx bounds checks to chain accessors in ssl.c. Harden SNI extension parser (TLSX_SNI_GetFromBuffer) with length checks preventing buffer overreads on malformed ClientHello. Fix off-by-one in TLSX_CSR_Free where <= should be < since csr->requests is a count, not a max index. Add remaining-buffer bounds checks to PKCS7 decoders: DecodeEnvelopedData, DecodeAuthEnvelopedData (encryptedContentSz and authTagSz), DecodeEncryptedData, SignedData null signature tag, and PwriKek_KeyUnWrap cekLen validation. --- src/internal.c | 5 +++-- src/ssl.c | 6 +++--- src/ssl_load.c | 7 +++++++ src/tls.c | 8 +++++++- src/tls13.c | 4 ++++ wolfcrypt/src/pkcs7.c | 12 ++++++++++++ 6 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/internal.c b/src/internal.c index 75c007c657..5f09158039 100644 --- a/src/internal.c +++ b/src/internal.c @@ -26208,7 +26208,8 @@ int SendCertificateStatus(WOLFSSL* ssl) } if (chain && chain->buffer) { - while (ret == 0 && idx + OPAQUE24_LEN < chain->length) { + while (ret == 0 && i < MAX_CHAIN_DEPTH && + idx + OPAQUE24_LEN < chain->length) { c24to32(chain->buffer + idx, &der.length); idx += OPAQUE24_LEN; @@ -26249,7 +26250,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; diff --git a/src/ssl.c b/src/ssl.c index efbbf3074e..2669f37aa1 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -14121,7 +14121,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; @@ -14132,7 +14132,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; @@ -14147,7 +14147,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); diff --git a/src/ssl_load.c b/src/ssl_load.c index 888c578334..494f290e3e 100644 --- a/src/ssl_load.c +++ b/src/ssl_load.c @@ -325,6 +325,13 @@ static int ProcessUserChain(WOLFSSL_CTX* ctx, WOLFSSL* ssl, while ((ret == 0) && (consumed < sz)) { DerBuffer* part = NULL; + /* Enforce maximum chain depth. */ + if (cnt >= MAX_CHAIN_DEPTH) { + WOLFSSL_MSG("Chain depth limit reached"); + ret = MAX_CHAIN_ERROR; + break; + } + /* Get a certificate as DER. */ ret = DataToDerBuffer(buff + consumed, (word32)(sz - consumed), format, type, info, heap, &part, NULL); diff --git a/src/tls.c b/src/tls.c index 62118d0678..53d2da7314 100644 --- a/src/tls.c +++ b/src/tls.c @@ -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; @@ -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; @@ -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; diff --git a/src/tls13.c b/src/tls13.c index 8d7efb9df4..3925e67b8f 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -9520,6 +9520,10 @@ static int SendTls13Certificate(WOLFSSL* ssl) } /* Certificate Data */ certSz = ssl->buffers.certificate->length; + if (ssl->buffers.certChainCnt > MAX_CHAIN_DEPTH) { + 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; diff --git a/wolfcrypt/src/pkcs7.c b/wolfcrypt/src/pkcs7.c index a4091890fd..bb42fca82d 100644 --- a/wolfcrypt/src/pkcs7.c +++ b/wolfcrypt/src/pkcs7.c @@ -14968,6 +14968,12 @@ int wc_PKCS7_DecodeAuthEnvelopedData(wc_PKCS7* pkcs7, byte* in, } } + if (ret == 0 && encryptedContentSz > (int)(pkiMsgSz - idx)) { + #ifdef NO_PKCS7_STREAM + ret = BUFFER_E; + #endif + } + if (ret < 0) break; @@ -15230,6 +15236,12 @@ authenv_atrbend: #endif idx = localIdx; + #ifdef NO_PKCS7_STREAM + if (ret == 0 && authTagSz > (word32)(pkiMsgSz - idx)) { + ret = BUFFER_E; + } + #endif + if (ret == 0 && authTagSz > (word32)sizeof(authTag)) { WOLFSSL_MSG("AuthEnvelopedData authTag too large for buffer"); ret = ASN_PARSE_E;