@@ -34282,40 +34282,62 @@ enum {
3428234282/* CRL Reason Code OID: 2.5.29.21 */
3428334283static const byte crlReasonOid[] = { 0x55, 0x1d, 0x15 };
3428434284
34285- /* Parse CRL entry extensions to extract the reason code.
34286- * Sets *reasonCode if found, otherwise leaves it unchanged. */
34287- static void ParseCRL_ReasonCode(const byte* buff, word32 idx, word32 maxIdx,
34288- int* reasonCode)
34285+ /* Parse CRL entry extensions.
34286+ * Extracts the reason code into *reasonCode if the CRL Reason extension
34287+ * is present. Per RFC 5280 Section 5.3, returns ASN_CRIT_EXT_E if any
34288+ * unknown extension is marked critical. Returns 0 on success. */
34289+ static int ParseCRL_EntryExtensions(const byte* buff, word32 idx, word32 maxIdx,
34290+ int* reasonCode)
3428934291{
3429034292 while (idx < maxIdx) {
3429134293 int len;
34294+ int oidLen;
3429234295 word32 end;
3429334296 word32 localIdx;
34297+ word32 oidContent;
3429434298 byte tag;
34299+ int critical = 0;
34300+ int isReasonOid = 0;
3429534301
3429634302 /* Each extension is a SEQUENCE */
3429734303 if (GetSequence(buff, &idx, &len, maxIdx) < 0) {
3429834304 break;
3429934305 }
3430034306 end = idx + (word32)len;
3430134307
34302- /* Check for CRL Reason OID: 2.5.29.21 */
34303- if (end - idx >= (word32)(2 + sizeof(crlReasonOid)) &&
34304- buff[idx] == ASN_OBJECT_ID &&
34305- buff[idx + 1] == sizeof(crlReasonOid) &&
34306- XMEMCMP(buff + idx + 2, crlReasonOid,
34308+ /* Parse OID: tag, length (short or long form), content */
34309+ if (GetASNTag(buff, &idx, &tag, end) < 0 ||
34310+ tag != ASN_OBJECT_ID) {
34311+ break;
34312+ }
34313+ if (GetLength(buff, &idx, &oidLen, end) < 0) {
34314+ break;
34315+ }
34316+ oidContent = idx;
34317+ if (idx + (word32)oidLen > end) {
34318+ break;
34319+ }
34320+
34321+ /* Check if it's the CRL Reason OID: 2.5.29.21 */
34322+ if ((word32)oidLen == sizeof(crlReasonOid) &&
34323+ XMEMCMP(buff + oidContent, crlReasonOid,
3430734324 sizeof(crlReasonOid)) == 0) {
34308- /* Skip past the OID */
34309- idx += 2 + (word32)sizeof(crlReasonOid);
34310- /* Skip optional critical BOOLEAN */
34311- localIdx = idx;
34312- if (GetASNTag(buff, &localIdx, &tag, end) == 0 &&
34313- tag == ASN_BOOLEAN) {
34314- /* Consume full BOOLEAN TLV (tag + length + value). */
34315- if (GetBoolean(buff, &idx, end) < 0) {
34316- break;
34317- }
34325+ isReasonOid = 1;
34326+ }
34327+ idx = oidContent + (word32)oidLen;
34328+
34329+ /* Parse optional critical BOOLEAN */
34330+ localIdx = idx;
34331+ if (GetASNTag(buff, &localIdx, &tag, end) == 0 &&
34332+ tag == ASN_BOOLEAN) {
34333+ int ret = GetBoolean(buff, &idx, end);
34334+ if (ret < 0) {
34335+ break;
3431834336 }
34337+ critical = ret;
34338+ }
34339+
34340+ if (isReasonOid) {
3431934341 /* Get OCTET STRING wrapping the ENUMERATED */
3432034342 if (GetOctetString(buff, &idx, &len, end) >= 0) {
3432134343 /* Parse ENUMERATED reason value */
@@ -34331,8 +34353,15 @@ static void ParseCRL_ReasonCode(const byte* buff, word32 idx, word32 maxIdx,
3433134353 }
3433234354 }
3433334355 }
34356+ else if (critical) {
34357+ /* RFC 5280 Section 5.3: reject CRL with unknown critical
34358+ * entry extension. */
34359+ WOLFSSL_MSG("Unknown critical CRL entry extension");
34360+ return ASN_CRIT_EXT_E;
34361+ }
3433434362 idx = end;
3433534363 }
34364+ return 0;
3433634365}
3433734366
3433834367#ifdef HAVE_CRL
@@ -34345,8 +34374,7 @@ WOLFSSL_TEST_VIS int wc_ParseCRLReasonFromExtensions(const byte* ext,
3434534374 return BAD_FUNC_ARG;
3434634375 }
3434734376
34348- ParseCRL_ReasonCode(ext, 0, extSz, reasonCode);
34349- return 0;
34377+ return ParseCRL_EntryExtensions(ext, 0, extSz, reasonCode);
3435034378}
3435134379#endif
3435234380
@@ -34409,49 +34437,58 @@ static int GetRevoked(RevokedCert* rcert, const byte* buff, word32* idx,
3440934437 /* Parse CRL entry extensions (v2 only) */
3441034438 if (dataASN[REVOKEDASN_IDX_TIME_EXT].length > 0) {
3441134439 word32 extOff = dataASN[REVOKEDASN_IDX_TIME_EXT].offset;
34412- word32 extLen = dataASN[REVOKEDASN_IDX_TIME_EXT].length;
34413- word32 extEnd = extOff + extLen;
34414- word32 extIdx2 = extOff;
34440+ word32 extTagEnd = extOff +
34441+ dataASN[REVOKEDASN_IDX_TIME_EXT].length + 6;
34442+ int extLen;
34443+
34444+ /* .offset points at the outer SEQUENCE tag. Re-parse the
34445+ * SEQUENCE header to locate the content start (list of
34446+ * Extension SEQUENCEs), which handles long-form length.
34447+ * extTagEnd adds 6 to cover the worst-case tag+long-form-length
34448+ * header for the outer SEQUENCE. */
34449+ if (GetSequence(buff, &extOff, &extLen, extTagEnd) < 0) {
34450+ ret = ASN_PARSE_E;
34451+ }
34452+ else {
34453+ word32 extEnd = extOff + (word32)extLen;
3441534454
3441634455#if defined(OPENSSL_EXTRA)
34417- /* Store raw DER of extensions for OpenSSL compat API.
34418- * Include the outer SEQUENCE tag+length. */
34419- {
34420- /* Back up to include the SEQUENCE header. We know the
34421- * content starts at extOff, so the header is just before.
34422- * Use the raw buffer start from before GetASN_Items. */
34423- word32 seqHdrSz = 0;
34424- /* The outer SEQUENCE header is at most 4 bytes before
34425- * content. Rather than guess, store just the content. */
34426- rc->extensions = (byte*)XMALLOC(extLen, dcrl->heap,
34456+ /* Store raw DER of extension contents for OpenSSL compat. */
34457+ rc->extensions = (byte*)XMALLOC((size_t)extLen, dcrl->heap,
3442734458 DYNAMIC_TYPE_REVOKED);
3442834459 if (rc->extensions != NULL) {
34429- XMEMCPY(rc->extensions, buff + extOff, extLen);
34430- rc->extensionsSz = extLen;
34460+ XMEMCPY(rc->extensions, buff + extOff, (size_t) extLen);
34461+ rc->extensionsSz = (word32) extLen;
3443134462 }
34432- (void)seqHdrSz;
34433- }
3443434463#endif
3443534464
34436- ParseCRL_ReasonCode(buff, extIdx2, extEnd, &rc->reasonCode);
34465+ ret = ParseCRL_EntryExtensions(buff, extOff, extEnd,
34466+ &rc->reasonCode);
34467+ }
3443734468 }
3443834469
34439- /* Add revoked certificate to chain. */
34470+ if (ret == 0) {
34471+ /* Add revoked certificate to chain. */
3444034472#ifndef CRL_STATIC_REVOKED_LIST
34441- rc->next = dcrl->certs;
34442- dcrl->certs = rc;
34473+ rc->next = dcrl->certs;
34474+ dcrl->certs = rc;
3444334475#endif
34444- dcrl->totalCerts++;
34476+ dcrl->totalCerts++;
34477+ }
3444534478 }
3444634479
3444734480 FREE_ASNGETDATA(dataASN, dcrl->heap);
34448- #ifndef CRL_STATIC_REVOKED_LIST
3444934481 if ((ret != 0) && (rc != NULL)) {
3445034482#if defined(OPENSSL_EXTRA)
3445134483 XFREE(rc->extensions, dcrl->heap, DYNAMIC_TYPE_REVOKED);
34484+ rc->extensions = NULL;
34485+ rc->extensionsSz = 0;
3445234486#endif
34487+ #ifndef CRL_STATIC_REVOKED_LIST
3445334488 XFREE(rc, dcrl->heap, DYNAMIC_TYPE_CRL);
34489+ #endif
3445434490 }
34491+ #ifndef CRL_STATIC_REVOKED_LIST
3445534492 (void)rcert;
3445634493#endif
3445734494 return ret;
@@ -34475,7 +34512,13 @@ static int ParseCRL_RevokedCerts(RevokedCert* rcert, DecodedCRL* dcrl,
3447534512 /* Parse each revoked certificate. */
3447634513 while ((ret == 0) && (idx < maxIdx)) {
3447734514 /* Parse a revoked certificate. */
34478- if (GetRevoked(rcert, buff, &idx, dcrl, maxIdx) < 0) {
34515+ int r = GetRevoked(rcert, buff, &idx, dcrl, maxIdx);
34516+ if (r == WC_NO_ERR_TRACE(ASN_CRIT_EXT_E)) {
34517+ /* Preserve the specific error so callers can distinguish a
34518+ * rejected critical extension from a generic parse failure. */
34519+ ret = r;
34520+ }
34521+ else if (r < 0) {
3447934522 ret = ASN_PARSE_E;
3448034523 }
3448134524 }
0 commit comments