@@ -34106,40 +34106,62 @@ enum {
3410634106/* CRL Reason Code OID: 2.5.29.21 */
3410734107static const byte crlReasonOid[] = { 0x55, 0x1d, 0x15 };
3410834108
34109- /* Parse CRL entry extensions to extract the reason code.
34110- * Sets *reasonCode if found, otherwise leaves it unchanged. */
34111- static void ParseCRL_ReasonCode(const byte* buff, word32 idx, word32 maxIdx,
34112- int* reasonCode)
34109+ /* Parse CRL entry extensions.
34110+ * Extracts the reason code into *reasonCode if the CRL Reason extension
34111+ * is present. Per RFC 5280 Section 5.3, returns ASN_CRIT_EXT_E if any
34112+ * unknown extension is marked critical. Returns 0 on success. */
34113+ static int ParseCRL_EntryExtensions(const byte* buff, word32 idx, word32 maxIdx,
34114+ int* reasonCode)
3411334115{
3411434116 while (idx < maxIdx) {
3411534117 int len;
34118+ int oidLen;
3411634119 word32 end;
3411734120 word32 localIdx;
34121+ word32 oidContent;
3411834122 byte tag;
34123+ int critical = 0;
34124+ int isReasonOid = 0;
3411934125
3412034126 /* Each extension is a SEQUENCE */
3412134127 if (GetSequence(buff, &idx, &len, maxIdx) < 0) {
3412234128 break;
3412334129 }
3412434130 end = idx + (word32)len;
3412534131
34126- /* Check for CRL Reason OID: 2.5.29.21 */
34127- if (end - idx >= (word32)(2 + sizeof(crlReasonOid)) &&
34128- buff[idx] == ASN_OBJECT_ID &&
34129- buff[idx + 1] == sizeof(crlReasonOid) &&
34130- XMEMCMP(buff + idx + 2, crlReasonOid,
34132+ /* Parse OID: tag, length (short or long form), content */
34133+ if (GetASNTag(buff, &idx, &tag, end) < 0 ||
34134+ tag != ASN_OBJECT_ID) {
34135+ break;
34136+ }
34137+ if (GetLength(buff, &idx, &oidLen, end) < 0) {
34138+ break;
34139+ }
34140+ oidContent = idx;
34141+ if (idx + (word32)oidLen > end) {
34142+ break;
34143+ }
34144+
34145+ /* Check if it's the CRL Reason OID: 2.5.29.21 */
34146+ if ((word32)oidLen == sizeof(crlReasonOid) &&
34147+ XMEMCMP(buff + oidContent, crlReasonOid,
3413134148 sizeof(crlReasonOid)) == 0) {
34132- /* Skip past the OID */
34133- idx += 2 + (word32)sizeof(crlReasonOid);
34134- /* Skip optional critical BOOLEAN */
34135- localIdx = idx;
34136- if (GetASNTag(buff, &localIdx, &tag, end) == 0 &&
34137- tag == ASN_BOOLEAN) {
34138- /* Consume full BOOLEAN TLV (tag + length + value). */
34139- if (GetBoolean(buff, &idx, end) < 0) {
34140- break;
34141- }
34149+ isReasonOid = 1;
34150+ }
34151+ idx = oidContent + (word32)oidLen;
34152+
34153+ /* Parse optional critical BOOLEAN */
34154+ localIdx = idx;
34155+ if (GetASNTag(buff, &localIdx, &tag, end) == 0 &&
34156+ tag == ASN_BOOLEAN) {
34157+ int ret = GetBoolean(buff, &idx, end);
34158+ if (ret < 0) {
34159+ break;
3414234160 }
34161+ critical = ret;
34162+ }
34163+
34164+ if (isReasonOid) {
3414334165 /* Get OCTET STRING wrapping the ENUMERATED */
3414434166 if (GetOctetString(buff, &idx, &len, end) >= 0) {
3414534167 /* Parse ENUMERATED reason value */
@@ -34155,8 +34177,15 @@ static void ParseCRL_ReasonCode(const byte* buff, word32 idx, word32 maxIdx,
3415534177 }
3415634178 }
3415734179 }
34180+ else if (critical) {
34181+ /* RFC 5280 Section 5.3: reject CRL with unknown critical
34182+ * entry extension. */
34183+ WOLFSSL_MSG("Unknown critical CRL entry extension");
34184+ return ASN_CRIT_EXT_E;
34185+ }
3415834186 idx = end;
3415934187 }
34188+ return 0;
3416034189}
3416134190
3416234191#ifdef HAVE_CRL
@@ -34169,8 +34198,7 @@ WOLFSSL_TEST_VIS int wc_ParseCRLReasonFromExtensions(const byte* ext,
3416934198 return BAD_FUNC_ARG;
3417034199 }
3417134200
34172- ParseCRL_ReasonCode(ext, 0, extSz, reasonCode);
34173- return 0;
34201+ return ParseCRL_EntryExtensions(ext, 0, extSz, reasonCode);
3417434202}
3417534203#endif
3417634204
@@ -34233,49 +34261,58 @@ static int GetRevoked(RevokedCert* rcert, const byte* buff, word32* idx,
3423334261 /* Parse CRL entry extensions (v2 only) */
3423434262 if (dataASN[REVOKEDASN_IDX_TIME_EXT].length > 0) {
3423534263 word32 extOff = dataASN[REVOKEDASN_IDX_TIME_EXT].offset;
34236- word32 extLen = dataASN[REVOKEDASN_IDX_TIME_EXT].length;
34237- word32 extEnd = extOff + extLen;
34238- word32 extIdx2 = extOff;
34264+ word32 extTagEnd = extOff +
34265+ dataASN[REVOKEDASN_IDX_TIME_EXT].length + 6;
34266+ int extLen;
34267+
34268+ /* .offset points at the outer SEQUENCE tag. Re-parse the
34269+ * SEQUENCE header to locate the content start (list of
34270+ * Extension SEQUENCEs), which handles long-form length.
34271+ * extTagEnd adds 6 to cover the worst-case tag+long-form-length
34272+ * header for the outer SEQUENCE. */
34273+ if (GetSequence(buff, &extOff, &extLen, extTagEnd) < 0) {
34274+ ret = ASN_PARSE_E;
34275+ }
34276+ else {
34277+ word32 extEnd = extOff + (word32)extLen;
3423934278
3424034279#if defined(OPENSSL_EXTRA)
34241- /* Store raw DER of extensions for OpenSSL compat API.
34242- * Include the outer SEQUENCE tag+length. */
34243- {
34244- /* Back up to include the SEQUENCE header. We know the
34245- * content starts at extOff, so the header is just before.
34246- * Use the raw buffer start from before GetASN_Items. */
34247- word32 seqHdrSz = 0;
34248- /* The outer SEQUENCE header is at most 4 bytes before
34249- * content. Rather than guess, store just the content. */
34250- rc->extensions = (byte*)XMALLOC(extLen, dcrl->heap,
34280+ /* Store raw DER of extension contents for OpenSSL compat. */
34281+ rc->extensions = (byte*)XMALLOC((size_t)extLen, dcrl->heap,
3425134282 DYNAMIC_TYPE_REVOKED);
3425234283 if (rc->extensions != NULL) {
34253- XMEMCPY(rc->extensions, buff + extOff, extLen);
34254- rc->extensionsSz = extLen;
34284+ XMEMCPY(rc->extensions, buff + extOff, (size_t) extLen);
34285+ rc->extensionsSz = (word32) extLen;
3425534286 }
34256- (void)seqHdrSz;
34257- }
3425834287#endif
3425934288
34260- ParseCRL_ReasonCode(buff, extIdx2, extEnd, &rc->reasonCode);
34289+ ret = ParseCRL_EntryExtensions(buff, extOff, extEnd,
34290+ &rc->reasonCode);
34291+ }
3426134292 }
3426234293
34263- /* Add revoked certificate to chain. */
34294+ if (ret == 0) {
34295+ /* Add revoked certificate to chain. */
3426434296#ifndef CRL_STATIC_REVOKED_LIST
34265- rc->next = dcrl->certs;
34266- dcrl->certs = rc;
34297+ rc->next = dcrl->certs;
34298+ dcrl->certs = rc;
3426734299#endif
34268- dcrl->totalCerts++;
34300+ dcrl->totalCerts++;
34301+ }
3426934302 }
3427034303
3427134304 FREE_ASNGETDATA(dataASN, dcrl->heap);
34272- #ifndef CRL_STATIC_REVOKED_LIST
3427334305 if ((ret != 0) && (rc != NULL)) {
3427434306#if defined(OPENSSL_EXTRA)
3427534307 XFREE(rc->extensions, dcrl->heap, DYNAMIC_TYPE_REVOKED);
34308+ rc->extensions = NULL;
34309+ rc->extensionsSz = 0;
3427634310#endif
34311+ #ifndef CRL_STATIC_REVOKED_LIST
3427734312 XFREE(rc, dcrl->heap, DYNAMIC_TYPE_CRL);
34313+ #endif
3427834314 }
34315+ #ifndef CRL_STATIC_REVOKED_LIST
3427934316 (void)rcert;
3428034317#endif
3428134318 return ret;
@@ -34299,7 +34336,13 @@ static int ParseCRL_RevokedCerts(RevokedCert* rcert, DecodedCRL* dcrl,
3429934336 /* Parse each revoked certificate. */
3430034337 while ((ret == 0) && (idx < maxIdx)) {
3430134338 /* Parse a revoked certificate. */
34302- if (GetRevoked(rcert, buff, &idx, dcrl, maxIdx) < 0) {
34339+ int r = GetRevoked(rcert, buff, &idx, dcrl, maxIdx);
34340+ if (r == WC_NO_ERR_TRACE(ASN_CRIT_EXT_E)) {
34341+ /* Preserve the specific error so callers can distinguish a
34342+ * rejected critical extension from a generic parse failure. */
34343+ ret = r;
34344+ }
34345+ else if (r < 0) {
3430334346 ret = ASN_PARSE_E;
3430434347 }
3430534348 }
0 commit comments