mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-05 12:10:51 +02:00
reject crls with unrecognized critical entry extensions per rfc 5280 section 5.3
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDGzCCAgOgAwIBAgIUHiXBXW8CIaDwFBWcO00dcxYA5FEwDQYJKoZIhvcNAQEL
|
||||
BQAwFTETMBEGA1UEAwwKY2xhaW0tcm9vdDAeFw0yNjA0MTYxMTM4NTVaFw0zNjA0
|
||||
MTMxMTM4NTVaMBUxEzARBgNVBAMMCmNsYWltLXJvb3QwggEiMA0GCSqGSIb3DQEB
|
||||
AQUAA4IBDwAwggEKAoIBAQC6SYy1F8EBJG9WGqk7A+KfJLElmPs4gnhUpx9ph+SW
|
||||
G4EYELDAW0u/uB307nUPtUVycM5lhEQ+MHjE8+y6lnikZfxijfUp+Xw9eGwdSkzJ
|
||||
FS0iEOqTJrimF9MOvAyrg2P2HMyDcyl+f4N/vWOqjfp4hdI+YJVajfqPzZQ/EyjZ
|
||||
0IcoF3jiYY15lwGpfITAHL5fXcooa17dg6VVNBG6+ouSo96287qrfxpn/W8ghUx3
|
||||
p46+uPiPcONa03fJnhBgtNsMxQXhH73mee6CP1F24n9cEW3TIWnsBRGLyDmzLKaD
|
||||
tG+sGdZqZQ0IlFjePckMVANzDI0kCfOxXdLj61bWKGZbAgMBAAGjYzBhMA8GA1Ud
|
||||
EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRSl1hHmMr4maB+
|
||||
jhw4Luq76pt0MDAfBgNVHSMEGDAWgBRSl1hHmMr4maB+jhw4Luq76pt0MDANBgkq
|
||||
hkiG9w0BAQsFAAOCAQEAfTDUagGJO2LtFkZZD/I7td6JCBdoy0bcOabLVrCR6wOy
|
||||
FcQ7TWNVIgom5mRG6A+o897hQ1Tm14r0T6tWkxJxSyVxCjEYee5FpPVyZ/pB2YeX
|
||||
Ce9VrW9HHVqy6fciBS1agajoU7CU9mP/P1F6CKwnmlcRIqQAhHCGdjkPT1fPjpTS
|
||||
jkPA1TR99aFFHrfIfnz+XU1TQyUVnggBVqT/eVklySOYrwWvwQsp8eLENjGR+vK7
|
||||
Euhn+cehXoztkhKjK+HC4aCwDhKn0KKu1vowIQ9z/iQhXwOGac3sdhjh/bZkkKYG
|
||||
LhlAk1A35JDjHweu+4nD9sSQq0BnTEMsorA+YRZpjw==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,11 @@
|
||||
-----BEGIN X509 CRL-----
|
||||
MIIBjDB2AgEBMA0GCSqGSIb3DQEBCwUAMBUxEzARBgNVBAMMCmNsYWltLXJvb3QX
|
||||
DTI2MDQyMTEyMzM0OFoXDTM2MDQxODEyMzM0OFowLTArAgIQABcNMjYwNDIxMTIz
|
||||
MzQ4WjAWMBQGA1UdAQEB/wQKDAhvYnNvbGV0ZTANBgkqhkiG9w0BAQsFAAOCAQEA
|
||||
PPG5bodrM2jK+6KcqRh9vEkWhLyxCkJij1om7R8BMO5bpvxZFOSqdN9GvIqYANYT
|
||||
ZNQzZZOer9DSXc1I9Cha179dyGnBsX33geSdhF99JxA/kZUVynfOn56El7iywkPQ
|
||||
yEpuY7uQQRfcp7D7tGz8S7mNhOMAeaN+jR2Psmn8q1Yc3W01vwYZos3qjHG1xDZ6
|
||||
97QVFX6tbnbeu/hBsF87oiAyCbXnhRQOvrVsKqjrZXGkfRKfJ1deHc1vWgio7fhr
|
||||
A1/Hb4sdfTWZUwtduTRI9Vyaw1IL41d+0ExdzTshIHbddB/dyByrRqlIzJdeiigt
|
||||
d1Gcf1EYSoWiV8Xaj9SFng==
|
||||
-----END X509 CRL-----
|
||||
@@ -27,7 +27,9 @@ EXTRA_DIST += \
|
||||
certs/crl/extra-crls/large_crlnum.pem \
|
||||
certs/crl/extra-crls/large_crlnum2.pem \
|
||||
certs/crl/extra-crls/crlnum_57oct.pem \
|
||||
certs/crl/extra-crls/crlnum_64oct.pem
|
||||
certs/crl/extra-crls/crlnum_64oct.pem \
|
||||
certs/crl/extra-crls/claim-root.pem \
|
||||
certs/crl/extra-crls/crl_critical_entry.pem
|
||||
|
||||
# Intermediate cert CRL's
|
||||
EXTRA_DIST += \
|
||||
|
||||
@@ -2427,6 +2427,31 @@ int test_wolfSSL_CRL_unknown_critical_ext(void)
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
int test_wolfSSL_CRL_unknown_critical_entry_ext(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if !defined(NO_CERTS) && defined(HAVE_CRL) && !defined(NO_RSA) && \
|
||||
!defined(NO_FILESYSTEM)
|
||||
WOLFSSL_CERT_MANAGER* cm = NULL;
|
||||
|
||||
ExpectNotNull(cm = wolfSSL_CertManagerNew());
|
||||
ExpectIntEQ(wolfSSL_CertManagerLoadCA(cm,
|
||||
"./certs/crl/extra-crls/claim-root.pem", NULL), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_CertManagerEnableCRL(cm, WOLFSSL_CRL_CHECKALL),
|
||||
WOLFSSL_SUCCESS);
|
||||
|
||||
/* CRL with a revoked entry that carries a critical unknown extension
|
||||
* (OID 2.5.29.1, old X.509v2 AKI, permanently superseded).
|
||||
* Per RFC 5280 Section 5.3, the CRL must not be used. */
|
||||
ExpectIntNE(wolfSSL_CertManagerLoadCRLFile(cm,
|
||||
"./certs/crl/extra-crls/crl_critical_entry.pem", WOLFSSL_FILETYPE_PEM),
|
||||
WOLFSSL_SUCCESS);
|
||||
|
||||
wolfSSL_CertManagerFree(cm);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
int test_wolfSSL_CertManagerCheckOCSPResponse(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
|
||||
@@ -42,6 +42,7 @@ int test_wolfSSL_CRL_static_revoked_list(void);
|
||||
int test_wolfSSL_CRL_duplicate_extensions(void);
|
||||
int test_wolfSSL_CRL_critical_idp(void);
|
||||
int test_wolfSSL_CRL_unknown_critical_ext(void);
|
||||
int test_wolfSSL_CRL_unknown_critical_entry_ext(void);
|
||||
int test_wolfSSL_CertManagerCheckOCSPResponse(void);
|
||||
int test_various_pathlen_chains(void);
|
||||
int test_wolfSSL_CertManagerRejectMD5Cert(void);
|
||||
@@ -65,6 +66,7 @@ int test_wolfSSL_CertManagerRejectMD5Cert(void);
|
||||
TEST_DECL_GROUP("certman", test_wolfSSL_CRL_duplicate_extensions), \
|
||||
TEST_DECL_GROUP("certman", test_wolfSSL_CRL_critical_idp), \
|
||||
TEST_DECL_GROUP("certman", test_wolfSSL_CRL_unknown_critical_ext), \
|
||||
TEST_DECL_GROUP("certman", test_wolfSSL_CRL_unknown_critical_entry_ext), \
|
||||
TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerCheckOCSPResponse), \
|
||||
TEST_DECL_GROUP("certman", test_various_pathlen_chains), \
|
||||
TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerRejectMD5Cert)
|
||||
|
||||
+88
-45
@@ -34282,16 +34282,22 @@ enum {
|
||||
/* CRL Reason Code OID: 2.5.29.21 */
|
||||
static const byte crlReasonOid[] = { 0x55, 0x1d, 0x15 };
|
||||
|
||||
/* Parse CRL entry extensions to extract the reason code.
|
||||
* Sets *reasonCode if found, otherwise leaves it unchanged. */
|
||||
static void ParseCRL_ReasonCode(const byte* buff, word32 idx, word32 maxIdx,
|
||||
int* reasonCode)
|
||||
/* Parse CRL entry extensions.
|
||||
* Extracts the reason code into *reasonCode if the CRL Reason extension
|
||||
* is present. Per RFC 5280 Section 5.3, returns ASN_CRIT_EXT_E if any
|
||||
* unknown extension is marked critical. Returns 0 on success. */
|
||||
static int ParseCRL_EntryExtensions(const byte* buff, word32 idx, word32 maxIdx,
|
||||
int* reasonCode)
|
||||
{
|
||||
while (idx < maxIdx) {
|
||||
int len;
|
||||
int oidLen;
|
||||
word32 end;
|
||||
word32 localIdx;
|
||||
word32 oidContent;
|
||||
byte tag;
|
||||
int critical = 0;
|
||||
int isReasonOid = 0;
|
||||
|
||||
/* Each extension is a SEQUENCE */
|
||||
if (GetSequence(buff, &idx, &len, maxIdx) < 0) {
|
||||
@@ -34299,23 +34305,39 @@ static void ParseCRL_ReasonCode(const byte* buff, word32 idx, word32 maxIdx,
|
||||
}
|
||||
end = idx + (word32)len;
|
||||
|
||||
/* Check for CRL Reason OID: 2.5.29.21 */
|
||||
if (end - idx >= (word32)(2 + sizeof(crlReasonOid)) &&
|
||||
buff[idx] == ASN_OBJECT_ID &&
|
||||
buff[idx + 1] == sizeof(crlReasonOid) &&
|
||||
XMEMCMP(buff + idx + 2, crlReasonOid,
|
||||
/* Parse OID: tag, length (short or long form), content */
|
||||
if (GetASNTag(buff, &idx, &tag, end) < 0 ||
|
||||
tag != ASN_OBJECT_ID) {
|
||||
break;
|
||||
}
|
||||
if (GetLength(buff, &idx, &oidLen, end) < 0) {
|
||||
break;
|
||||
}
|
||||
oidContent = idx;
|
||||
if (idx + (word32)oidLen > end) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check if it's the CRL Reason OID: 2.5.29.21 */
|
||||
if ((word32)oidLen == sizeof(crlReasonOid) &&
|
||||
XMEMCMP(buff + oidContent, crlReasonOid,
|
||||
sizeof(crlReasonOid)) == 0) {
|
||||
/* Skip past the OID */
|
||||
idx += 2 + (word32)sizeof(crlReasonOid);
|
||||
/* Skip optional critical BOOLEAN */
|
||||
localIdx = idx;
|
||||
if (GetASNTag(buff, &localIdx, &tag, end) == 0 &&
|
||||
tag == ASN_BOOLEAN) {
|
||||
/* Consume full BOOLEAN TLV (tag + length + value). */
|
||||
if (GetBoolean(buff, &idx, end) < 0) {
|
||||
break;
|
||||
}
|
||||
isReasonOid = 1;
|
||||
}
|
||||
idx = oidContent + (word32)oidLen;
|
||||
|
||||
/* Parse optional critical BOOLEAN */
|
||||
localIdx = idx;
|
||||
if (GetASNTag(buff, &localIdx, &tag, end) == 0 &&
|
||||
tag == ASN_BOOLEAN) {
|
||||
int ret = GetBoolean(buff, &idx, end);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
critical = ret;
|
||||
}
|
||||
|
||||
if (isReasonOid) {
|
||||
/* Get OCTET STRING wrapping the ENUMERATED */
|
||||
if (GetOctetString(buff, &idx, &len, end) >= 0) {
|
||||
/* Parse ENUMERATED reason value */
|
||||
@@ -34331,8 +34353,15 @@ static void ParseCRL_ReasonCode(const byte* buff, word32 idx, word32 maxIdx,
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (critical) {
|
||||
/* RFC 5280 Section 5.3: reject CRL with unknown critical
|
||||
* entry extension. */
|
||||
WOLFSSL_MSG("Unknown critical CRL entry extension");
|
||||
return ASN_CRIT_EXT_E;
|
||||
}
|
||||
idx = end;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_CRL
|
||||
@@ -34345,8 +34374,7 @@ WOLFSSL_TEST_VIS int wc_ParseCRLReasonFromExtensions(const byte* ext,
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
ParseCRL_ReasonCode(ext, 0, extSz, reasonCode);
|
||||
return 0;
|
||||
return ParseCRL_EntryExtensions(ext, 0, extSz, reasonCode);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -34409,49 +34437,58 @@ static int GetRevoked(RevokedCert* rcert, const byte* buff, word32* idx,
|
||||
/* Parse CRL entry extensions (v2 only) */
|
||||
if (dataASN[REVOKEDASN_IDX_TIME_EXT].length > 0) {
|
||||
word32 extOff = dataASN[REVOKEDASN_IDX_TIME_EXT].offset;
|
||||
word32 extLen = dataASN[REVOKEDASN_IDX_TIME_EXT].length;
|
||||
word32 extEnd = extOff + extLen;
|
||||
word32 extIdx2 = extOff;
|
||||
word32 extTagEnd = extOff +
|
||||
dataASN[REVOKEDASN_IDX_TIME_EXT].length + 6;
|
||||
int extLen;
|
||||
|
||||
/* .offset points at the outer SEQUENCE tag. Re-parse the
|
||||
* SEQUENCE header to locate the content start (list of
|
||||
* Extension SEQUENCEs), which handles long-form length.
|
||||
* extTagEnd adds 6 to cover the worst-case tag+long-form-length
|
||||
* header for the outer SEQUENCE. */
|
||||
if (GetSequence(buff, &extOff, &extLen, extTagEnd) < 0) {
|
||||
ret = ASN_PARSE_E;
|
||||
}
|
||||
else {
|
||||
word32 extEnd = extOff + (word32)extLen;
|
||||
|
||||
#if defined(OPENSSL_EXTRA)
|
||||
/* Store raw DER of extensions for OpenSSL compat API.
|
||||
* Include the outer SEQUENCE tag+length. */
|
||||
{
|
||||
/* Back up to include the SEQUENCE header. We know the
|
||||
* content starts at extOff, so the header is just before.
|
||||
* Use the raw buffer start from before GetASN_Items. */
|
||||
word32 seqHdrSz = 0;
|
||||
/* The outer SEQUENCE header is at most 4 bytes before
|
||||
* content. Rather than guess, store just the content. */
|
||||
rc->extensions = (byte*)XMALLOC(extLen, dcrl->heap,
|
||||
/* Store raw DER of extension contents for OpenSSL compat. */
|
||||
rc->extensions = (byte*)XMALLOC((size_t)extLen, dcrl->heap,
|
||||
DYNAMIC_TYPE_REVOKED);
|
||||
if (rc->extensions != NULL) {
|
||||
XMEMCPY(rc->extensions, buff + extOff, extLen);
|
||||
rc->extensionsSz = extLen;
|
||||
XMEMCPY(rc->extensions, buff + extOff, (size_t)extLen);
|
||||
rc->extensionsSz = (word32)extLen;
|
||||
}
|
||||
(void)seqHdrSz;
|
||||
}
|
||||
#endif
|
||||
|
||||
ParseCRL_ReasonCode(buff, extIdx2, extEnd, &rc->reasonCode);
|
||||
ret = ParseCRL_EntryExtensions(buff, extOff, extEnd,
|
||||
&rc->reasonCode);
|
||||
}
|
||||
}
|
||||
|
||||
/* Add revoked certificate to chain. */
|
||||
if (ret == 0) {
|
||||
/* Add revoked certificate to chain. */
|
||||
#ifndef CRL_STATIC_REVOKED_LIST
|
||||
rc->next = dcrl->certs;
|
||||
dcrl->certs = rc;
|
||||
rc->next = dcrl->certs;
|
||||
dcrl->certs = rc;
|
||||
#endif
|
||||
dcrl->totalCerts++;
|
||||
dcrl->totalCerts++;
|
||||
}
|
||||
}
|
||||
|
||||
FREE_ASNGETDATA(dataASN, dcrl->heap);
|
||||
#ifndef CRL_STATIC_REVOKED_LIST
|
||||
if ((ret != 0) && (rc != NULL)) {
|
||||
#if defined(OPENSSL_EXTRA)
|
||||
XFREE(rc->extensions, dcrl->heap, DYNAMIC_TYPE_REVOKED);
|
||||
rc->extensions = NULL;
|
||||
rc->extensionsSz = 0;
|
||||
#endif
|
||||
#ifndef CRL_STATIC_REVOKED_LIST
|
||||
XFREE(rc, dcrl->heap, DYNAMIC_TYPE_CRL);
|
||||
#endif
|
||||
}
|
||||
#ifndef CRL_STATIC_REVOKED_LIST
|
||||
(void)rcert;
|
||||
#endif
|
||||
return ret;
|
||||
@@ -34475,7 +34512,13 @@ static int ParseCRL_RevokedCerts(RevokedCert* rcert, DecodedCRL* dcrl,
|
||||
/* Parse each revoked certificate. */
|
||||
while ((ret == 0) && (idx < maxIdx)) {
|
||||
/* Parse a revoked certificate. */
|
||||
if (GetRevoked(rcert, buff, &idx, dcrl, maxIdx) < 0) {
|
||||
int r = GetRevoked(rcert, buff, &idx, dcrl, maxIdx);
|
||||
if (r == WC_NO_ERR_TRACE(ASN_CRIT_EXT_E)) {
|
||||
/* Preserve the specific error so callers can distinguish a
|
||||
* rejected critical extension from a generic parse failure. */
|
||||
ret = r;
|
||||
}
|
||||
else if (r < 0) {
|
||||
ret = ASN_PARSE_E;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9113,18 +9113,17 @@ static int GetRevoked(RevokedCert* rcert, const byte* buff, word32* idx,
|
||||
XFREE(rc, dcrl->heap, DYNAMIC_TYPE_REVOKED);
|
||||
return ret;
|
||||
}
|
||||
/* add to list */
|
||||
rc->next = dcrl->certs;
|
||||
dcrl->certs = rc;
|
||||
|
||||
(void)rcert;
|
||||
#endif /* CRL_STATIC_REVOKED_LIST */
|
||||
dcrl->totalCerts++;
|
||||
/* get date */
|
||||
#ifndef NO_ASN_TIME
|
||||
ret = GetBasicDate(buff, idx, rc->revDate, &rc->revDateFormat, maxIdx);
|
||||
if (ret < 0) {
|
||||
WOLFSSL_MSG("Expecting Date");
|
||||
#ifndef CRL_STATIC_REVOKED_LIST
|
||||
XFREE(rc, dcrl->heap, DYNAMIC_TYPE_REVOKED);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
@@ -9158,11 +9157,30 @@ static int GetRevoked(RevokedCert* rcert, const byte* buff, word32* idx,
|
||||
}
|
||||
#endif
|
||||
|
||||
ParseCRL_ReasonCode(buff, seqIdx, extEnd, &rc->reasonCode);
|
||||
ret = ParseCRL_EntryExtensions(buff, seqIdx, extEnd,
|
||||
&rc->reasonCode);
|
||||
if (ret != 0) {
|
||||
#if defined(OPENSSL_EXTRA)
|
||||
XFREE(rc->extensions, dcrl->heap, DYNAMIC_TYPE_REVOKED);
|
||||
rc->extensions = NULL;
|
||||
rc->extensionsSz = 0;
|
||||
#endif
|
||||
#ifndef CRL_STATIC_REVOKED_LIST
|
||||
XFREE(rc, dcrl->heap, DYNAMIC_TYPE_REVOKED);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef CRL_STATIC_REVOKED_LIST
|
||||
/* add to list only after all parsing succeeded */
|
||||
rc->next = dcrl->certs;
|
||||
dcrl->certs = rc;
|
||||
#endif
|
||||
dcrl->totalCerts++;
|
||||
|
||||
*idx = end;
|
||||
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user