@@ -34653,40 +34653,62 @@ enum {
3465334653/* CRL Reason Code OID: 2.5.29.21 */
3465434654static const byte crlReasonOid[] = { 0x55, 0x1d, 0x15 };
3465534655
34656- /* Parse CRL entry extensions to extract the reason code.
34657- * Sets *reasonCode if found, otherwise leaves it unchanged. */
34658- static void ParseCRL_ReasonCode(const byte* buff, word32 idx, word32 maxIdx,
34659- int* reasonCode)
34656+ /* Parse CRL entry extensions.
34657+ * Extracts the reason code into *reasonCode if the CRL Reason extension
34658+ * is present. Per RFC 5280 Section 5.3, returns ASN_CRIT_EXT_E if any
34659+ * unknown extension is marked critical. Returns 0 on success. */
34660+ static int ParseCRL_EntryExtensions(const byte* buff, word32 idx, word32 maxIdx,
34661+ int* reasonCode)
3466034662{
3466134663 while (idx < maxIdx) {
3466234664 int len;
34665+ int oidLen;
3466334666 word32 end;
3466434667 word32 localIdx;
34668+ word32 oidContent;
3466534669 byte tag;
34670+ int critical = 0;
34671+ int isReasonOid = 0;
3466634672
3466734673 /* Each extension is a SEQUENCE */
3466834674 if (GetSequence(buff, &idx, &len, maxIdx) < 0) {
3466934675 break;
3467034676 }
3467134677 end = idx + (word32)len;
3467234678
34673- /* Check for CRL Reason OID: 2.5.29.21 */
34674- if (end - idx >= (word32)(2 + sizeof(crlReasonOid)) &&
34675- buff[idx] == ASN_OBJECT_ID &&
34676- buff[idx + 1] == sizeof(crlReasonOid) &&
34677- XMEMCMP(buff + idx + 2, crlReasonOid,
34679+ /* Parse OID: tag, length (short or long form), content */
34680+ if (GetASNTag(buff, &idx, &tag, end) < 0 ||
34681+ tag != ASN_OBJECT_ID) {
34682+ break;
34683+ }
34684+ if (GetLength(buff, &idx, &oidLen, end) < 0) {
34685+ break;
34686+ }
34687+ oidContent = idx;
34688+ if (idx + (word32)oidLen > end) {
34689+ break;
34690+ }
34691+
34692+ /* Check if it's the CRL Reason OID: 2.5.29.21 */
34693+ if ((word32)oidLen == sizeof(crlReasonOid) &&
34694+ XMEMCMP(buff + oidContent, crlReasonOid,
3467834695 sizeof(crlReasonOid)) == 0) {
34679- /* Skip past the OID */
34680- idx += 2 + (word32)sizeof(crlReasonOid);
34681- /* Skip optional critical BOOLEAN */
34682- localIdx = idx;
34683- if (GetASNTag(buff, &localIdx, &tag, end) == 0 &&
34684- tag == ASN_BOOLEAN) {
34685- /* Consume full BOOLEAN TLV (tag + length + value). */
34686- if (GetBoolean(buff, &idx, end) < 0) {
34687- break;
34688- }
34696+ isReasonOid = 1;
34697+ }
34698+ idx = oidContent + (word32)oidLen;
34699+
34700+ /* Parse optional critical BOOLEAN */
34701+ localIdx = idx;
34702+ if (GetASNTag(buff, &localIdx, &tag, end) == 0 &&
34703+ tag == ASN_BOOLEAN) {
34704+ int ret = GetBoolean(buff, &idx, end);
34705+ if (ret < 0) {
34706+ break;
3468934707 }
34708+ critical = ret;
34709+ }
34710+
34711+ if (isReasonOid) {
3469034712 /* Get OCTET STRING wrapping the ENUMERATED */
3469134713 if (GetOctetString(buff, &idx, &len, end) >= 0) {
3469234714 /* Parse ENUMERATED reason value */
@@ -34702,8 +34724,15 @@ static void ParseCRL_ReasonCode(const byte* buff, word32 idx, word32 maxIdx,
3470234724 }
3470334725 }
3470434726 }
34727+ else if (critical) {
34728+ /* RFC 5280 Section 5.3: reject CRL with unknown critical
34729+ * entry extension. */
34730+ WOLFSSL_MSG("Unknown critical CRL entry extension");
34731+ return ASN_CRIT_EXT_E;
34732+ }
3470534733 idx = end;
3470634734 }
34735+ return 0;
3470734736}
3470834737
3470934738#ifdef HAVE_CRL
@@ -34716,8 +34745,7 @@ WOLFSSL_TEST_VIS int wc_ParseCRLReasonFromExtensions(const byte* ext,
3471634745 return BAD_FUNC_ARG;
3471734746 }
3471834747
34719- ParseCRL_ReasonCode(ext, 0, extSz, reasonCode);
34720- return 0;
34748+ return ParseCRL_EntryExtensions(ext, 0, extSz, reasonCode);
3472134749}
3472234750#endif
3472334751
@@ -34780,49 +34808,58 @@ static int GetRevoked(RevokedCert* rcert, const byte* buff, word32* idx,
3478034808 /* Parse CRL entry extensions (v2 only) */
3478134809 if (dataASN[REVOKEDASN_IDX_TIME_EXT].length > 0) {
3478234810 word32 extOff = dataASN[REVOKEDASN_IDX_TIME_EXT].offset;
34783- word32 extLen = dataASN[REVOKEDASN_IDX_TIME_EXT].length;
34784- word32 extEnd = extOff + extLen;
34785- word32 extIdx2 = extOff;
34811+ word32 extTagEnd = extOff +
34812+ dataASN[REVOKEDASN_IDX_TIME_EXT].length + 6;
34813+ int extLen;
34814+
34815+ /* .offset points at the outer SEQUENCE tag. Re-parse the
34816+ * SEQUENCE header to locate the content start (list of
34817+ * Extension SEQUENCEs), which handles long-form length.
34818+ * extTagEnd adds 6 to cover the worst-case tag+long-form-length
34819+ * header for the outer SEQUENCE. */
34820+ if (GetSequence(buff, &extOff, &extLen, extTagEnd) < 0) {
34821+ ret = ASN_PARSE_E;
34822+ }
34823+ else {
34824+ word32 extEnd = extOff + (word32)extLen;
3478634825
3478734826#if defined(OPENSSL_EXTRA)
34788- /* Store raw DER of extensions for OpenSSL compat API.
34789- * Include the outer SEQUENCE tag+length. */
34790- {
34791- /* Back up to include the SEQUENCE header. We know the
34792- * content starts at extOff, so the header is just before.
34793- * Use the raw buffer start from before GetASN_Items. */
34794- word32 seqHdrSz = 0;
34795- /* The outer SEQUENCE header is at most 4 bytes before
34796- * content. Rather than guess, store just the content. */
34797- rc->extensions = (byte*)XMALLOC(extLen, dcrl->heap,
34827+ /* Store raw DER of extension contents for OpenSSL compat. */
34828+ rc->extensions = (byte*)XMALLOC((size_t)extLen, dcrl->heap,
3479834829 DYNAMIC_TYPE_REVOKED);
3479934830 if (rc->extensions != NULL) {
34800- XMEMCPY(rc->extensions, buff + extOff, extLen);
34801- rc->extensionsSz = extLen;
34831+ XMEMCPY(rc->extensions, buff + extOff, (size_t) extLen);
34832+ rc->extensionsSz = (word32) extLen;
3480234833 }
34803- (void)seqHdrSz;
34804- }
3480534834#endif
3480634835
34807- ParseCRL_ReasonCode(buff, extIdx2, extEnd, &rc->reasonCode);
34836+ ret = ParseCRL_EntryExtensions(buff, extOff, extEnd,
34837+ &rc->reasonCode);
34838+ }
3480834839 }
3480934840
34810- /* Add revoked certificate to chain. */
34841+ if (ret == 0) {
34842+ /* Add revoked certificate to chain. */
3481134843#ifndef CRL_STATIC_REVOKED_LIST
34812- rc->next = dcrl->certs;
34813- dcrl->certs = rc;
34844+ rc->next = dcrl->certs;
34845+ dcrl->certs = rc;
3481434846#endif
34815- dcrl->totalCerts++;
34847+ dcrl->totalCerts++;
34848+ }
3481634849 }
3481734850
3481834851 FREE_ASNGETDATA(dataASN, dcrl->heap);
34819- #ifndef CRL_STATIC_REVOKED_LIST
3482034852 if ((ret != 0) && (rc != NULL)) {
3482134853#if defined(OPENSSL_EXTRA)
3482234854 XFREE(rc->extensions, dcrl->heap, DYNAMIC_TYPE_REVOKED);
34855+ rc->extensions = NULL;
34856+ rc->extensionsSz = 0;
3482334857#endif
34858+ #ifndef CRL_STATIC_REVOKED_LIST
3482434859 XFREE(rc, dcrl->heap, DYNAMIC_TYPE_CRL);
34860+ #endif
3482534861 }
34862+ #ifndef CRL_STATIC_REVOKED_LIST
3482634863 (void)rcert;
3482734864#endif
3482834865 return ret;
@@ -34846,7 +34883,13 @@ static int ParseCRL_RevokedCerts(RevokedCert* rcert, DecodedCRL* dcrl,
3484634883 /* Parse each revoked certificate. */
3484734884 while ((ret == 0) && (idx < maxIdx)) {
3484834885 /* Parse a revoked certificate. */
34849- if (GetRevoked(rcert, buff, &idx, dcrl, maxIdx) < 0) {
34886+ int r = GetRevoked(rcert, buff, &idx, dcrl, maxIdx);
34887+ if (r == WC_NO_ERR_TRACE(ASN_CRIT_EXT_E)) {
34888+ /* Preserve the specific error so callers can distinguish a
34889+ * rejected critical extension from a generic parse failure. */
34890+ ret = r;
34891+ }
34892+ else if (r < 0) {
3485034893 ret = ASN_PARSE_E;
3485134894 }
3485234895 }
0 commit comments