Skip to content

Commit 4738958

Browse files
Fix CRL distribution point scope check logic in crl_crldp_check (#3105)
Commit authored by @nebeid. ### Description of changes: A logic error in `crl_crldp_check()` (`crypto/x509/x509_vfy.c`) prevents CRL distribution point matching from ever running for normal certificates. When a CRL has an Issuing Distribution Point (IDP) extension, the CRL is incorrectly considered out of scope and a revoked certificate escapes detection. Three bugs in one condition: 1. `&&` should be `||` — the comment says skip DPs with `reasons` OR `CRLissuer`, but the code only triggers when BOTH are present. 2. `return 1` should be `continue` — when the condition matches, the code declares the CRL in scope instead of skipping the DP. 3. `idp_check_dp` is in the wrong branch — it only runs for DPs with `reasons`+`CRLissuer`, never for normal clean DPs. ### Fix Took upstream commit 5386d90. ### Testing Two test scenarios added in `crypto/x509/x509_test.cc`: **Scenario 1: Cert with a single clean CRLDP + CRL with matching IDP** Leaf has a clean CRLDP (distpoint URI only, no `reasons`, no `CRLissuer`). CRL has a matching IDP and revokes the leaf's serial. - Before fix: `idp_check_dp` is never called for clean DPs → CRL is out-of-scope. - After fix: `idp_check_dp` matches the distpoints → CRL in scope → `CERT_REVOKED`. **Scenario 2: Cert with two DPs + two CRLs** Leaf has two distribution points: - DP1: `distpoint` matching CRL-B IDP + `reasons` + `CRLissuer` (should be skipped) - DP2: clean `distpoint` (matches the revoking CRL-A) CRL-A (matching IDP) revokes the leaf. CRL-B (other IDP) has no revocations. - Before fix: - DP1 has `reasons`+`CRLissuer` so the `&&` condition is true. `idp_check_dp` matches DP1 against CRL-B → `return 1` → CRL-B is in scope → no revocations → cert appears valid. - DP2 never gets checked against CRL-A; it's skipped both by the `&&` check and by the fallback because it has an IDP. - After fix: - DP1 is skipped (`||` catches `reasons`). - DP2 matches CRL-A via `idp_check_dp` → `CERT_REVOKED`. PoC output without fix: ``` Scenario 1: Cert with clean CRLDP (distpoint only) + CRL with matching IDP Result: 44 (Different CRL scope) FAIL: Expected CERT_REVOKED (23), got 44 Scenario 2: Cert with two DPs (reasons+CRLissuer DP and clean DP) + two CRLs Result: 0 (ok) FAIL: Expected CERT_REVOKED (23), got 0 ``` PoC output with fix: ``` Scenario 1: Cert with clean CRLDP (distpoint only) + CRL with matching IDP Result: 23 (certificate revoked) PASS: Revoked cert correctly detected Scenario 2: Cert with two DPs (reasons+CRLissuer DP and clean DP) + two CRLs Result: 23 (certificate revoked) PASS: Revoked cert correctly detected ``` By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and the ISC license. Co-authored-by: Nevine Ebeid <66388554+nebeid@users.noreply.github.com>
1 parent 2aa5224 commit 4738958

File tree

2 files changed

+279
-36
lines changed

2 files changed

+279
-36
lines changed

crypto/x509/x509_test.cc

Lines changed: 261 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -359,35 +359,37 @@ Rvvdqakendy6WgHn1peoChj5w8SjHlbifINI2xYaHPUdfvGULUvPciLB
359359
-----END PRIVATE KEY-----
360360
)";
361361

362-
// kCRLTestRoot is a test root certificate. It has private key:
363-
//
364-
// -----BEGIN RSA PRIVATE KEY-----
365-
// MIIEpAIBAAKCAQEAo16WiLWZuaymsD8n5SKPmxV1y6jjgr3BS/dUBpbrzd1aeFzN
366-
// lI8l2jfAnzUyp+I21RQ+nh/MhqjGElkTtK9xMn1Y+S9GMRh+5R/Du0iCb1tCZIPY
367-
// 07Tgrb0KMNWe0v2QKVVruuYSgxIWodBfxlKO64Z8AJ5IbnWpuRqO6rctN9qUoMlT
368-
// IAB6dL4G0tDJ/PGFWOJYwOMEIX54bly2wgyYJVBKiRRt4f7n8H922qmvPNA9idmX
369-
// 9G1VAtgV6x97XXi7ULORIQvn9lVQF6nTYDBJhyuPB+mLThbLP2o9orxGx7aCtnnB
370-
// ZUIxUvHNOI0FaSaZH7Fi0xsZ/GkG2HZe7ImPJwIDAQABAoIBAQCJF9MTHfHGkk+/
371-
// DwCXlA0Wg0e6hBuHl10iNobYkMWIl/xXjOknhYiqOqb181py76472SVC5ERprC+r
372-
// Lf0PXzqKuA117mnkwT2bYLCL9Skf8WEhoFLQNbVlloF6wYjqXcYgKYKh8HgQbZl4
373-
// aLg2YQl2NADTNABsUWj/4H2WEelsODVviqfFs725lFg9KHDI8zxAZXLzDt/M9uVL
374-
// GxJiX12tr0AwaeAFZ1oPM/y+LznM3N3+Ht3jHHw3jZ/u8Z1RdAmdpu3bZ6tbwGBr
375-
// 9edsH5rKkm9aBvMrY7eX5VHqaqyRNFyG152ZOJh4XiiFG7EmgTPCpaHo50Y018Re
376-
// grVtk+FBAoGBANY3lY+V8ZOwMxSHes+kTnoimHO5Ob7nxrOC71i27x+4HHsYUeAr
377-
// /zOOghiDIn+oNkuiX5CIOWZKx159Bp65CPpCbTb/fh+HYnSgXFgCw7XptycO7LXM
378-
// 5GwR5jSfpfzBFdYxjxoUzDMFBwTEYRTm0HkUHkH+s+ajjw5wqqbcGLcfAoGBAMM8
379-
// DKW6Tb66xsf708f0jonAjKYTLZ+WOcwsBEWSFHoY8dUjvW5gqx5acHTEsc5ZTeh4
380-
// BCFLa+Mn9cuJWVJNs09k7Xb2PNl92HQ4GN2vbdkJhExbkT6oLDHg1hVD0w8KLfz1
381-
// lTAW6pS+6CdOHMEJpvqx89EgU/1GgIQ1fXYczE75AoGAKeJoXdDFkUjsU+FBhAPu
382-
// TDcjc80Nm2QaF9NMFR5/lsYa236f06MGnQAKM9zADBHJu/Qdl1brUjLg1HrBppsr
383-
// RDNkw1IlSOjhuUf5hkPUHGd8Jijm440SRIcjabqla8wdBupdvo2+d2NOQgJbsQiI
384-
// ToQ+fkzcxAXK3Nnuo/1436UCgYBjLH7UNOZHS8OsVM0I1r8NVKVdu4JCfeJQR8/H
385-
// s2P5ffBir+wLRMnH+nMDreMQiibcPxMCArkERAlE4jlgaJ38Z62E76KLbLTmnJRt
386-
// EC9Bv+bXjvAiHvWMRMUbOj/ddPNVez7Uld+FvdBaHwDWQlvzHzBWfBCOKSEhh7Z6
387-
// qDhUqQKBgQDPMDx2i5rfmQp3imV9xUcCkIRsyYQVf8Eo7NV07IdUy/otmksgn4Zt
388-
// Lbf3v2dvxOpTNTONWjp2c+iUQo8QxJCZr5Sfb21oQ9Ktcrmc/CY7LeBVDibXwxdM
389-
// vRG8kBzvslFWh7REzC3u06GSVhyKDfW93kN2cKVwGoahRlhj7oHuZQ==
390-
// -----END RSA PRIVATE KEY-----
362+
// kCRLTestRootKey is the private key for kCRLTestRoot.
363+
static const char kCRLTestRootKey[] = R"(
364+
-----BEGIN RSA PRIVATE KEY-----
365+
MIIEpAIBAAKCAQEAo16WiLWZuaymsD8n5SKPmxV1y6jjgr3BS/dUBpbrzd1aeFzN
366+
lI8l2jfAnzUyp+I21RQ+nh/MhqjGElkTtK9xMn1Y+S9GMRh+5R/Du0iCb1tCZIPY
367+
07Tgrb0KMNWe0v2QKVVruuYSgxIWodBfxlKO64Z8AJ5IbnWpuRqO6rctN9qUoMlT
368+
IAB6dL4G0tDJ/PGFWOJYwOMEIX54bly2wgyYJVBKiRRt4f7n8H922qmvPNA9idmX
369+
9G1VAtgV6x97XXi7ULORIQvn9lVQF6nTYDBJhyuPB+mLThbLP2o9orxGx7aCtnnB
370+
ZUIxUvHNOI0FaSaZH7Fi0xsZ/GkG2HZe7ImPJwIDAQABAoIBAQCJF9MTHfHGkk+/
371+
DwCXlA0Wg0e6hBuHl10iNobYkMWIl/xXjOknhYiqOqb181py76472SVC5ERprC+r
372+
Lf0PXzqKuA117mnkwT2bYLCL9Skf8WEhoFLQNbVlloF6wYjqXcYgKYKh8HgQbZl4
373+
aLg2YQl2NADTNABsUWj/4H2WEelsODVviqfFs725lFg9KHDI8zxAZXLzDt/M9uVL
374+
GxJiX12tr0AwaeAFZ1oPM/y+LznM3N3+Ht3jHHw3jZ/u8Z1RdAmdpu3bZ6tbwGBr
375+
9edsH5rKkm9aBvMrY7eX5VHqaqyRNFyG152ZOJh4XiiFG7EmgTPCpaHo50Y018Re
376+
grVtk+FBAoGBANY3lY+V8ZOwMxSHes+kTnoimHO5Ob7nxrOC71i27x+4HHsYUeAr
377+
/zOOghiDIn+oNkuiX5CIOWZKx159Bp65CPpCbTb/fh+HYnSgXFgCw7XptycO7LXM
378+
5GwR5jSfpfzBFdYxjxoUzDMFBwTEYRTm0HkUHkH+s+ajjw5wqqbcGLcfAoGBAMM8
379+
DKW6Tb66xsf708f0jonAjKYTLZ+WOcwsBEWSFHoY8dUjvW5gqx5acHTEsc5ZTeh4
380+
BCFLa+Mn9cuJWVJNs09k7Xb2PNl92HQ4GN2vbdkJhExbkT6oLDHg1hVD0w8KLfz1
381+
lTAW6pS+6CdOHMEJpvqx89EgU/1GgIQ1fXYczE75AoGAKeJoXdDFkUjsU+FBhAPu
382+
TDcjc80Nm2QaF9NMFR5/lsYa236f06MGnQAKM9zADBHJu/Qdl1brUjLg1HrBppsr
383+
RDNkw1IlSOjhuUf5hkPUHGd8Jijm440SRIcjabqla8wdBupdvo2+d2NOQgJbsQiI
384+
ToQ+fkzcxAXK3Nnuo/1436UCgYBjLH7UNOZHS8OsVM0I1r8NVKVdu4JCfeJQR8/H
385+
s2P5ffBir+wLRMnH+nMDreMQiibcPxMCArkERAlE4jlgaJ38Z62E76KLbLTmnJRt
386+
EC9Bv+bXjvAiHvWMRMUbOj/ddPNVez7Uld+FvdBaHwDWQlvzHzBWfBCOKSEhh7Z6
387+
qDhUqQKBgQDPMDx2i5rfmQp3imV9xUcCkIRsyYQVf8Eo7NV07IdUy/otmksgn4Zt
388+
Lbf3v2dvxOpTNTONWjp2c+iUQo8QxJCZr5Sfb21oQ9Ktcrmc/CY7LeBVDibXwxdM
389+
vRG8kBzvslFWh7REzC3u06GSVhyKDfW93kN2cKVwGoahRlhj7oHuZQ==
390+
-----END RSA PRIVATE KEY-----
391+
)";
392+
391393
static const char kCRLTestRoot[] = R"(
392394
-----BEGIN CERTIFICATE-----
393395
MIIDbzCCAlegAwIBAgIJAODri7v0dDUFMA0GCSqGSIb3DQEBCwUAME4xCzAJBgNV
@@ -2043,6 +2045,236 @@ TEST(X509Test, TestCRL) {
20432045
ASSERT_EQ(nullptr, X509_OBJECT_get0_X509_CRL(&invalidCRL));
20442046
}
20452047

2048+
// Helper to create a GENERAL_NAME with a URI.
2049+
static bssl::UniquePtr<GENERAL_NAME> MakeURIGeneralName(const char *uri) {
2050+
bssl::UniquePtr<GENERAL_NAME> name(GENERAL_NAME_new());
2051+
if (!name) {
2052+
return nullptr;
2053+
}
2054+
name->type = GEN_URI;
2055+
name->d.uniformResourceIdentifier = ASN1_IA5STRING_new();
2056+
if (!name->d.uniformResourceIdentifier ||
2057+
!ASN1_STRING_set(name->d.uniformResourceIdentifier, uri, strlen(uri))) {
2058+
return nullptr;
2059+
}
2060+
return name;
2061+
}
2062+
2063+
// Helper to create a DIST_POINT_NAME from a URI. Caller takes ownership.
2064+
static DIST_POINT_NAME *MakeDistPointName(const char *uri) {
2065+
DIST_POINT_NAME *dpn = DIST_POINT_NAME_new();
2066+
if (!dpn) {
2067+
return nullptr;
2068+
}
2069+
dpn->type = 0; // fullname
2070+
dpn->name.fullname = sk_GENERAL_NAME_new_null();
2071+
if (!dpn->name.fullname) {
2072+
DIST_POINT_NAME_free(dpn);
2073+
return nullptr;
2074+
}
2075+
auto gn = MakeURIGeneralName(uri);
2076+
if (!gn || !bssl::PushToStack(dpn->name.fullname, std::move(gn))) {
2077+
DIST_POINT_NAME_free(dpn);
2078+
return nullptr;
2079+
}
2080+
return dpn;
2081+
}
2082+
2083+
// Helper to create a leaf cert with a CRLDP extension and sign it.
2084+
static bssl::UniquePtr<X509> MakeCRLDPLeaf(
2085+
X509 *issuer_cert, EVP_PKEY *issuer_key, int serial,
2086+
CRL_DIST_POINTS *crldp) {
2087+
bssl::UniquePtr<EVP_PKEY> leaf_key(EVP_PKEY_new());
2088+
bssl::UniquePtr<RSA> rsa(RSA_new());
2089+
bssl::UniquePtr<BIGNUM> e(BN_new());
2090+
if (!leaf_key || !rsa || !e ||
2091+
!BN_set_word(e.get(), RSA_F4) ||
2092+
!RSA_generate_key_ex(rsa.get(), 2048, e.get(), nullptr) ||
2093+
!EVP_PKEY_assign_RSA(leaf_key.get(), rsa.release())) {
2094+
return nullptr;
2095+
}
2096+
bssl::UniquePtr<X509> leaf(X509_new());
2097+
if (!leaf ||
2098+
!X509_set_version(leaf.get(), X509_VERSION_3) ||
2099+
!X509_set_issuer_name(leaf.get(),
2100+
X509_get_subject_name(issuer_cert)) ||
2101+
!X509_NAME_add_entry_by_txt(
2102+
X509_get_subject_name(leaf.get()), "CN", MBSTRING_UTF8,
2103+
reinterpret_cast<const uint8_t *>("Leaf"), -1, -1, 0) ||
2104+
!X509_set_pubkey(leaf.get(), leaf_key.get()) ||
2105+
!ASN1_TIME_adj(X509_getm_notBefore(leaf.get()), kReferenceTime, -1, 0) ||
2106+
!ASN1_TIME_adj(X509_getm_notAfter(leaf.get()), kReferenceTime, 1, 0)) {
2107+
return nullptr;
2108+
}
2109+
bssl::UniquePtr<ASN1_INTEGER> sn(ASN1_INTEGER_new());
2110+
if (!sn || !ASN1_INTEGER_set(sn.get(), serial) ||
2111+
!X509_set_serialNumber(leaf.get(), sn.get())) {
2112+
return nullptr;
2113+
}
2114+
if (!X509_add1_ext_i2d(leaf.get(), NID_crl_distribution_points, crldp,
2115+
/*crit=*/0, /*flags=*/0)) {
2116+
return nullptr;
2117+
}
2118+
if (!X509_sign(leaf.get(), issuer_key, EVP_sha256())) {
2119+
return nullptr;
2120+
}
2121+
return leaf;
2122+
}
2123+
2124+
// Helper to create a CRL, optionally with an IDP extension and revoked serials.
2125+
static bssl::UniquePtr<X509_CRL> MakeTestCRL(
2126+
X509 *issuer_cert, EVP_PKEY *key, const char *idp_uri,
2127+
const std::vector<int> &revoked_serials) {
2128+
bssl::UniquePtr<X509_CRL> crl(X509_CRL_new());
2129+
if (!crl) {
2130+
return nullptr;
2131+
}
2132+
if (!X509_CRL_set_version(crl.get(), X509_CRL_VERSION_2) ||
2133+
!X509_CRL_set_issuer_name(crl.get(),
2134+
X509_get_subject_name(issuer_cert))) {
2135+
return nullptr;
2136+
}
2137+
bssl::UniquePtr<ASN1_TIME> last_update(ASN1_TIME_new());
2138+
if (!last_update ||
2139+
!ASN1_TIME_set_posix(last_update.get(), kReferenceTime) ||
2140+
!X509_CRL_set1_lastUpdate(crl.get(), last_update.get())) {
2141+
return nullptr;
2142+
}
2143+
bssl::UniquePtr<ASN1_TIME> next_update(ASN1_TIME_new());
2144+
if (!next_update ||
2145+
!ASN1_TIME_adj(next_update.get(), kReferenceTime, 30, 0) ||
2146+
!X509_CRL_set1_nextUpdate(crl.get(), next_update.get())) {
2147+
return nullptr;
2148+
}
2149+
for (int serial : revoked_serials) {
2150+
bssl::UniquePtr<X509_REVOKED> rev(X509_REVOKED_new());
2151+
bssl::UniquePtr<ASN1_INTEGER> sn(ASN1_INTEGER_new());
2152+
bssl::UniquePtr<ASN1_TIME> rev_time(ASN1_TIME_new());
2153+
if (!rev || !sn || !rev_time ||
2154+
!ASN1_INTEGER_set(sn.get(), serial) ||
2155+
!X509_REVOKED_set_serialNumber(rev.get(), sn.get()) ||
2156+
!ASN1_TIME_set_posix(rev_time.get(), kReferenceTime) ||
2157+
!X509_REVOKED_set_revocationDate(rev.get(), rev_time.get()) ||
2158+
!X509_CRL_add0_revoked(crl.get(), rev.get())) {
2159+
return nullptr;
2160+
}
2161+
rev.release(); // Ownership transferred to crl.
2162+
}
2163+
if (idp_uri) {
2164+
ISSUING_DIST_POINT *idp = ISSUING_DIST_POINT_new();
2165+
if (!idp) {
2166+
return nullptr;
2167+
}
2168+
idp->distpoint = MakeDistPointName(idp_uri);
2169+
if (!idp->distpoint ||
2170+
!X509_CRL_add1_ext_i2d(crl.get(), NID_issuing_distribution_point,
2171+
idp, /*crit=*/1, /*flags=*/0)) {
2172+
ISSUING_DIST_POINT_free(idp);
2173+
return nullptr;
2174+
}
2175+
ISSUING_DIST_POINT_free(idp);
2176+
}
2177+
if (!X509_CRL_sign(crl.get(), key, EVP_sha256())) {
2178+
return nullptr;
2179+
}
2180+
// Re-encode and re-parse so that internal fields like crl->idp and
2181+
// crl->idp_flags are populated from the IDP extension. These are only
2182+
// set during parsing (ASN1_OP_D2I_POST), not programmatic construction.
2183+
uint8_t *der = nullptr;
2184+
int der_len = i2d_X509_CRL(crl.get(), &der);
2185+
if (der_len <= 0) {
2186+
return nullptr;
2187+
}
2188+
const uint8_t *inp = der;
2189+
crl.reset(d2i_X509_CRL(nullptr, &inp, der_len));
2190+
OPENSSL_free(der);
2191+
return crl;
2192+
}
2193+
2194+
// Test that CRL distribution point scope checking (crl_crldp_check) correctly
2195+
// matches a cert's CRLDP against a CRL's Issuing Distribution Point (IDP).
2196+
TEST(X509Test, CRLDistributionPointScope) {
2197+
bssl::UniquePtr<X509> root(CertFromPEM(kCRLTestRoot));
2198+
bssl::UniquePtr<EVP_PKEY> key(PrivateKeyFromPEM(kCRLTestRootKey));
2199+
ASSERT_TRUE(root);
2200+
ASSERT_TRUE(key);
2201+
2202+
const int kLeafSerial = 0x1000;
2203+
const char *kCRLURI = "http://example.com/crl.pem";
2204+
const char *kOtherURI = "http://other.example.com/crl.pem";
2205+
2206+
// Scenario 1: Leaf with a single clean CRLDP (distpoint URI only, no reasons,
2207+
// no CRLissuer). CRL has a matching IDP and revokes the leaf's serial.
2208+
// Verify that the CRL is considered in scope and the cert is revoked.
2209+
{
2210+
bssl::UniquePtr<CRL_DIST_POINTS> crldp(sk_DIST_POINT_new_null());
2211+
ASSERT_TRUE(crldp);
2212+
bssl::UniquePtr<DIST_POINT> dp(DIST_POINT_new());
2213+
ASSERT_TRUE(dp);
2214+
dp->distpoint = MakeDistPointName(kCRLURI);
2215+
ASSERT_TRUE(dp->distpoint);
2216+
ASSERT_TRUE(bssl::PushToStack(crldp.get(), std::move(dp)));
2217+
2218+
auto leaf = MakeCRLDPLeaf(root.get(), key.get(), kLeafSerial, crldp.get());
2219+
ASSERT_TRUE(leaf);
2220+
auto crl = MakeTestCRL(root.get(), key.get(), kCRLURI, {kLeafSerial});
2221+
ASSERT_TRUE(crl);
2222+
2223+
EXPECT_EQ(X509_V_ERR_CERT_REVOKED,
2224+
Verify(leaf.get(), {root.get()}, {root.get()},
2225+
{crl.get()}, X509_V_FLAG_CRL_CHECK));
2226+
}
2227+
2228+
// Scenario 2: Leaf with two DPs:
2229+
// DP1: distpoint=kOtherURI + reasons + CRLissuer (should be skipped)
2230+
// DP2: distpoint=kCRLURI (clean, matches the revoking CRL)
2231+
// CRL-A (IDP=kCRLURI) revokes the leaf. CRL-B (IDP=kOtherURI) has no
2232+
// revocations. DP1 should be skipped and DP2 should match CRL-A.
2233+
{
2234+
bssl::UniquePtr<CRL_DIST_POINTS> crldp(sk_DIST_POINT_new_null());
2235+
ASSERT_TRUE(crldp);
2236+
2237+
// DP1: distpoint + reasons + CRLissuer
2238+
bssl::UniquePtr<DIST_POINT> dp1(DIST_POINT_new());
2239+
ASSERT_TRUE(dp1);
2240+
dp1->distpoint = MakeDistPointName(kOtherURI);
2241+
ASSERT_TRUE(dp1->distpoint);
2242+
dp1->reasons = ASN1_BIT_STRING_new();
2243+
ASSERT_TRUE(dp1->reasons);
2244+
ASN1_BIT_STRING_set_bit(dp1->reasons, 1, 1); // keyCompromise
2245+
dp1->CRLissuer = sk_GENERAL_NAME_new_null();
2246+
ASSERT_TRUE(dp1->CRLissuer);
2247+
bssl::UniquePtr<GENERAL_NAME> issuer_name(GENERAL_NAME_new());
2248+
ASSERT_TRUE(issuer_name);
2249+
issuer_name->type = GEN_DIRNAME;
2250+
issuer_name->d.directoryName = X509_NAME_new();
2251+
ASSERT_TRUE(issuer_name->d.directoryName);
2252+
ASSERT_TRUE(X509_NAME_add_entry_by_txt(
2253+
issuer_name->d.directoryName, "O", MBSTRING_ASC,
2254+
reinterpret_cast<const uint8_t *>("Other Issuer"), -1, -1, 0));
2255+
ASSERT_TRUE(bssl::PushToStack(dp1->CRLissuer, std::move(issuer_name)));
2256+
ASSERT_TRUE(bssl::PushToStack(crldp.get(), std::move(dp1)));
2257+
2258+
// DP2: clean distpoint only
2259+
bssl::UniquePtr<DIST_POINT> dp2(DIST_POINT_new());
2260+
ASSERT_TRUE(dp2);
2261+
dp2->distpoint = MakeDistPointName(kCRLURI);
2262+
ASSERT_TRUE(dp2->distpoint);
2263+
ASSERT_TRUE(bssl::PushToStack(crldp.get(), std::move(dp2)));
2264+
2265+
auto leaf = MakeCRLDPLeaf(root.get(), key.get(), kLeafSerial, crldp.get());
2266+
ASSERT_TRUE(leaf);
2267+
auto crl_a = MakeTestCRL(root.get(), key.get(), kCRLURI, {kLeafSerial});
2268+
ASSERT_TRUE(crl_a);
2269+
auto crl_b = MakeTestCRL(root.get(), key.get(), kOtherURI, {});
2270+
ASSERT_TRUE(crl_b);
2271+
2272+
EXPECT_EQ(X509_V_ERR_CERT_REVOKED,
2273+
Verify(leaf.get(), {root.get()}, {root.get()},
2274+
{crl_a.get(), crl_b.get()}, X509_V_FLAG_CRL_CHECK));
2275+
}
2276+
}
2277+
20462278
TEST(X509Test, TestX509GettersSetters) {
20472279
bssl::UniquePtr<X509_OBJECT> obj(X509_OBJECT_new());
20482280
bssl::UniquePtr<X509> x509(CertFromPEM(kCRLTestRoot));

crypto/x509/x509_vfy.c

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,7 +1167,8 @@ static int idp_check_dp(DIST_POINT_NAME *a, DIST_POINT_NAME *b) {
11671167
return 0;
11681168
}
11691169

1170-
// Check CRLDP and IDP
1170+
// Check CRLDP and IDP. Return true when the CRL is a good
1171+
// candidate CRL from which to check revocation of the certificate.
11711172
static int crl_crldp_check(X509 *x, X509_CRL *crl, int crl_score) {
11721173
if (crl->idp_flags & IDP_ONLYATTR) {
11731174
return 0;
@@ -1190,18 +1191,28 @@ static int crl_crldp_check(X509 *x, X509_CRL *crl, int crl_score) {
11901191
//
11911192
// We also do not support indirect CRLs, and a CRL issuer can only match
11921193
// indirect CRLs (RFC 5280, section 6.3.3, step b.1).
1193-
// support.
1194-
if (dp->reasons != NULL && dp->CRLissuer != NULL &&
1195-
(!crl->idp || idp_check_dp(dp->distpoint, crl->idp->distpoint))) {
1194+
if (dp->reasons != NULL || dp->CRLissuer != NULL) {
1195+
continue;
1196+
}
1197+
// At this point we have already checked that the CRL issuer matches
1198+
// the certificate issuer (and set CRL_SCORE_ISSUER_NAME);
1199+
1200+
// RFC 5280 Section 6.3.3 step b.2
1201+
if (!crl->idp || idp_check_dp(dp->distpoint, crl->idp->distpoint)){
11961202
return 1;
11971203
}
11981204
}
11991205

12001206
// If the CRL does not specify an issuing distribution point, allow it to
12011207
// match anything.
1202-
//
1203-
// TODO(davidben): Does this match RFC 5280? It's hard to follow because RFC
1204-
// 5280 starts from distribution points, while this starts from CRLs.
1208+
// RFC5280 section 6.3.3 check (b).(2) does not prescribe what to do if the
1209+
// CRL does not include an IDP. This fallback returns true if the CRL did not
1210+
// include an IDP or an IDP without a DP. Such a CRL could still be a good
1211+
// candidate CRL to check against although we cannot check if it matches the
1212+
// DP in the certificate. In the event of multiple good candidate CRLs
1213+
// (crl_crldp_check() returns 1 and get_crl_score() scores them high), some
1214+
// without an IDP or with an IDP and without a DP and others matching the
1215+
// certificate's DP, get_crl_sk() will pick the freshest one.
12051216
return !crl->idp || !crl->idp->distpoint;
12061217
}
12071218

0 commit comments

Comments
 (0)