Commit 5a3723e254 for openssl.org
commit 5a3723e2543684f86ad54ee8ae65db7556cbccd3
Author: OpenSSL Machine <openssl-machine@openssl.org>
Date: Thu Apr 30 20:46:41 2026 +0900
Reject delta CRLs as complete CRL candidates
get_crl_score() is used when selecting a complete/base CRL. Its delta CRL rejection was chained after the extended CRL and IDP reason handling, so it could be skipped when extended CRL support was disabled, or when an IDP onlySomeReasons branch was taken.
As a result, a CRL with a Delta CRL Indicator could be scored as a complete/base CRL candidate. Since a delta CRL contains only changes relative to a base CRL, this could cause a previously revoked certificate to be accepted as valid when only the delta CRL is presented to the verifier.
Reject CRLs with base_crl_number unconditionally in get_crl_score() before IDP reason filtering. Delta CRLs are still considered by get_delta_sk() after a complete CRL is selected and check_delta_base() confirms compatibility.
Add verify recipe coverage for a delta CRL being rejected as a complete CRL, and for a delta CRL with IssuingDistributionPoint.onlySomeReasons being rejected under -extended_crl.
Reported-by: jujerpig (GitHub issue #31040)
Fixes #31040
Reviewed-by: Bob Beck <beck@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
MergeDate: Wed Jun 17 08:53:02 2026
(Merged from https://github.com/openssl/openssl/pull/31044)
diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c
index dccdf6f499..977672d8fc 100644
--- a/crypto/x509/x509_vfy.c
+++ b/crypto/x509/x509_vfy.c
@@ -1693,6 +1693,12 @@ static int get_crl_score(X509_STORE_CTX *ctx, X509 **pissuer,
/* Invalid IDP cannot be processed */
if ((crl->idp_flags & IDP_INVALID) != 0)
return 0;
+ /*
+ * Reject delta CRLs unconditionally here. They are considered by
+ * get_delta_sk() after a base CRL is selected.
+ */
+ if (crl->base_crl_number != NULL)
+ return 0;
/* Reason codes or indirect CRLs need extended CRL support */
if ((ctx->param->flags & X509_V_FLAG_EXTENDED_CRL_SUPPORT) == 0) {
if (crl->idp_flags & (IDP_INDIRECT | IDP_REASONS))
@@ -1702,9 +1708,6 @@ static int get_crl_score(X509_STORE_CTX *ctx, X509 **pissuer,
if ((crl->idp_reasons & ~tmp_reasons) == 0)
return 0;
}
- /* Don't process deltas at this stage */
- else if (crl->base_crl_number != NULL)
- return 0;
/* If issuer name doesn't match certificate need indirect CRL */
if (X509_NAME_cmp(X509_get_issuer_name(x), X509_CRL_get_issuer(crl)) != 0) {
if ((crl->idp_flags & IDP_INDIRECT) == 0)
diff --git a/test/certs/delta-crl-as-complete-ca.pem b/test/certs/delta-crl-as-complete-ca.pem
new file mode 100644
index 0000000000..c7bade031a
--- /dev/null
+++ b/test/certs/delta-crl-as-complete-ca.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDQTCCAimgAwIBAgIUP+A4l1Xr6j1/lQ/bRBdNebfmEHMwDQYJKoZIhvcNAQEL
+BQAwKDEmMCQGA1UEAwwdRGVsdGEgQ1JMIGFzIENvbXBsZXRlIFRlc3QgQ0EwHhcN
+MjYwNDMwMTQ0NjAyWhcNMzYwNDI3MTQ0NjAyWjAoMSYwJAYDVQQDDB1EZWx0YSBD
+UkwgYXMgQ29tcGxldGUgVGVzdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBAJ6Wr3/CguoFSKWbeAE2Czjp6qFRdZ3xhhz27NuSdnHpoZE7xgzbt3Z2
+cNt+szZXZIgOyMzSOu0r8q0fKp4VJmR9M5KoMduTHRJpwvT79VhJdZED/1akKhgJ
+7WttsvK5kNc+1he7gELY3ssuzfw7MmZv+vMpykjNhSSBKpmUy2t9lX/b1mJ+LK6v
+gHQ09ntxdIGniRauzf027ugG72oSydk80YG0EHBlI19eYVhBJEWjLq4afLUYRbfm
+0OYHJGq5TE0VTww4xa1IzLFt4NpqveYztYN4ujKd95vbHB6EzCs3R15uJwwm/1px
+PXFhwp4MF/W422/XxcadU3+0zHDgEScCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB
+/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMIh2AkMIUaBjaMHDAOEBAv33XzE
+MB8GA1UdIwQYMBaAFMIh2AkMIUaBjaMHDAOEBAv33XzEMA0GCSqGSIb3DQEBCwUA
+A4IBAQA6yelNaY6aVomoTwmvNXdfNqlU96HGmEFfKMMFL1CgF5KsBaz4ARCJ+83s
+/A0+HufwFYOwUx+dkklatp791leaFjxkFSHmKrE6WVBnOoss7M4a+Hwei13Qrirx
+vKFhQde45OFqh8zpl0fru/KdCnbRoM05KVhFqnXR+vyDvghvlIWDFEFdX7KOdVXz
+s9v5ECR4kftEalZVq7WNVBS9afVPoQvYj3dcebMH8l8RL5+adV2NRvBKgUKo2kwy
+TG6nqWKRr4xqsARLPxYFk1tHbKfIVWy1FrABb5lW8iMA/RZCv6Qzyqbr4nT0WDTa
+g3OUp6sSDEhnLYhnW3bsLUdpgRfy
+-----END CERTIFICATE-----
diff --git a/test/certs/delta-crl-as-complete-delta-reasons.pem b/test/certs/delta-crl-as-complete-delta-reasons.pem
new file mode 100644
index 0000000000..d20bc63f53
--- /dev/null
+++ b/test/certs/delta-crl-as-complete-delta-reasons.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBxDCBrQIBATANBgkqhkiG9w0BAQsFADAoMSYwJAYDVQQDDB1EZWx0YSBDUkwg
+YXMgQ29tcGxldGUgVGVzdCBDQRcNMjYwNDMwMTQ0NjAzWhcNMzYwNDI3MTQ0NjAz
+WqBRME8wHwYDVR0jBBgwFoAUwiHYCQwhRoGNowcMA4QEC/fdfMQwDQYDVR0bAQH/
+BAMCAQEwEAYDVR0cAQH/BAYwBIMCBWAwCwYDVR0UBAQCAjAAMA0GCSqGSIb3DQEB
+CwUAA4IBAQB735x+EogYnkeL3DHwHBbTKXyWMp9UEdR9DVUVElNISNrLTuOy62Nr
+N4OEWtDCESltII739hryz85lg0Jo1jBOYbRdGYIacRzm1WUvtk3aLfGt0gwifFtW
+4AiiQZUz3jq0F2V9TythzE0nMQbIiXpG3ACc+HQ5/gwpFEvw9ABjXMp5SVU47fT5
+M2jRk12XR6N/MDJB13uh0EH814CD23Gqhvu2lVL3KSGiwImirbQX18egibBmIykR
+jqwaH0giFa5ZbrMcOyBksyZJIZgAOpnzn4M5FlQb4s0AjxYSRjqQWV5qrBVfWT6b
+Jy+lW3HgXMYyZt6Gqyayfu7N+6JbqhQ+
+-----END X509 CRL-----
diff --git a/test/certs/delta-crl-as-complete-delta.pem b/test/certs/delta-crl-as-complete-delta.pem
new file mode 100644
index 0000000000..301a6b009a
--- /dev/null
+++ b/test/certs/delta-crl-as-complete-delta.pem
@@ -0,0 +1,12 @@
+-----BEGIN X509 CRL-----
+MIIBsjCBmwIBATANBgkqhkiG9w0BAQsFADAoMSYwJAYDVQQDDB1EZWx0YSBDUkwg
+YXMgQ29tcGxldGUgVGVzdCBDQRcNMjYwNDMwMTQ0NjAzWhcNMzYwNDI3MTQ0NjAz
+WqA/MD0wHwYDVR0jBBgwFoAUwiHYCQwhRoGNowcMA4QEC/fdfMQwDQYDVR0bAQH/
+BAMCAQEwCwYDVR0UBAQCAiAAMA0GCSqGSIb3DQEBCwUAA4IBAQADs08Ab6TRWijd
+dOsW3wBCzWVi/GYlyPhMZjcPTDuM939rpL7yOONM7OIgekP3BYM7g4Dvp6Q4Caul
+yZTtJ87dxqP56e11Q7h5eVcBBWxc1dM7FwMffqXXSEApuKdkRqKNAqtXugkgozdM
+YBNIuSP+gyLNt4vrxtQS26pBMYJnbCf7ye5dyJFK3G58o97VknNMrYewPrzj6LVG
+abS3eJnDaGtiGkQ16pOsKNRTlQYlXBk/9NMq8N8ntsm2ri3LJ/JQHRIHxxSsKl+f
+ZgKmOa160pz+xWNq7edIM0RpwGjLVViE7F22y2JH/VJcqXimsl506QykD9COCzH6
+Lnv//55C
+-----END X509 CRL-----
diff --git a/test/certs/delta-crl-as-complete-leaf.pem b/test/certs/delta-crl-as-complete-leaf.pem
new file mode 100644
index 0000000000..c8ffebdb64
--- /dev/null
+++ b/test/certs/delta-crl-as-complete-leaf.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDQDCCAiigAwIBAgIUXolrPqGDnGH9gUx2N5WFjfUkXHgwDQYJKoZIhvcNAQEL
+BQAwKDEmMCQGA1UEAwwdRGVsdGEgQ1JMIGFzIENvbXBsZXRlIFRlc3QgQ0EwHhcN
+MjYwNDMwMTQ0NjAzWhcNMzYwNDI3MTQ0NjAzWjAqMSgwJgYDVQQDDB9EZWx0YSBD
+UkwgYXMgQ29tcGxldGUgVGVzdCBMZWFmMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEA43iL7Otyb0SaPVDmOVBs39LnH9vlO2sV+ZPqV4o1a/JhCOzokMxg
+NuPqCamMEufKctaKE8q/LKKEpu3QAG7H1vlsu2qfQLojFuHNKDnHM7RbsRNp9azE
+DWpBWs0mXL96LUJzseioIIqFdSH9HkcTyrKXvr8iqaOUC96Vu9F03Ws59FmaTwK6
+JmKThfrIY2edSRyEO6tmVTh0XLW0PyNbSM/2eOKmh92RCbhKDjgCeNhPuJ0WRDAX
+zNF+cgRO48epsl6ec03DdBSCFcwc5qL+TkzXjXIQbXhIsvl/OKNG0BUXDeYqH7ln
+wEO6d556CF6wJlCd0DqzSzPsoVIo96asKwIDAQABo2AwXjAMBgNVHRMBAf8EAjAA
+MA4GA1UdDwEB/wQEAwIHgDAdBgNVHQ4EFgQUFPI2A7WhMsPK15SsH1W+CiEKqA0w
+HwYDVR0jBBgwFoAUwiHYCQwhRoGNowcMA4QEC/fdfMQwDQYJKoZIhvcNAQELBQAD
+ggEBAGX7FpwNoq62mhlJbksgAJcaw5ci2m1CGFgNfh+f6pdA0mG3ywyM7eEZcr6h
+GFslaDsG8m2O0l737Xs7mBpyq0ruxjpvk62VdgwTUkeZzz8gnuBIWg/+zrWyYCI7
+uDX4wKVMma9MF52YUHhNTdxvV0EE4wuUcYMAlWrCCzxkf4eNxyDJGBXli04xSpSj
+cG9SsDRyjQKc9lFFqrQ9P/DVL4CDUSovE/2DWdZmS3RmsHjhNdGjpWtpoEsxBHdQ
+5lyeGDp0fBf4RNWRsdr8RUsSiDFUproRibZ9/3uzH5yAfivZmWlVRUX/eyyjCrZD
+VuW2H/npgPe+QSkwlUXYsZ1vqiM=
+-----END CERTIFICATE-----
diff --git a/test/recipes/25-test_verify.t b/test/recipes/25-test_verify.t
index 30a637a3e9..9564e78153 100644
--- a/test/recipes/25-test_verify.t
+++ b/test/recipes/25-test_verify.t
@@ -1,5 +1,5 @@
#! /usr/bin/env perl
-# Copyright 2015-2025 The OpenSSL Project Authors. All Rights Reserved.
+# Copyright 2015-2026 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the Apache License 2.0 (the "License"). You may not use
# this file except in compliance with the License. You can obtain a copy
@@ -30,7 +30,7 @@ sub verify {
run(app([@args]));
}
-plan tests => 217;
+plan tests => 219;
# Canonical success
ok(verify("ee-cert", "sslserver", ["root-cert"], ["ca-cert"]),
@@ -628,6 +628,38 @@ run(app(["openssl", "verify",
ok(grep(/CRL is not yet valid/, do { open my $fh, '<', $cve_28388_stderr; <$fh> }),
"CVE-2026-28388");
+# Delta CRLs must not be accepted as complete CRLs
+my $delta_crl_as_complete_stderr = "delta-crl-as-complete.err";
+ok(!run(app(["openssl", "verify", "-auth_level", "1",
+ "-CAfile",
+ srctop_file(@certspath, "delta-crl-as-complete-ca.pem"),
+ "-no_check_time", "-crl_check",
+ "-CRLfile",
+ srctop_file(@certspath, "delta-crl-as-complete-delta.pem"),
+ srctop_file(@certspath, "delta-crl-as-complete-leaf.pem")],
+ stderr => $delta_crl_as_complete_stderr))
+ && grep(/unable to get certificate CRL/,
+ do { open my $fh, '<', $delta_crl_as_complete_stderr; <$fh> }),
+ "Delta CRL is not accepted as complete CRL");
+
+my $delta_crl_as_complete_reasons_stderr =
+ "delta-crl-as-complete-reasons.err";
+ok(!run(app(["openssl", "verify", "-auth_level", "1",
+ "-CAfile",
+ srctop_file(@certspath, "delta-crl-as-complete-ca.pem"),
+ "-no_check_time", "-crl_check", "-extended_crl",
+ "-CRLfile",
+ srctop_file(@certspath,
+ "delta-crl-as-complete-delta-reasons.pem"),
+ srctop_file(@certspath, "delta-crl-as-complete-leaf.pem")],
+ stderr => $delta_crl_as_complete_reasons_stderr))
+ && grep(/unable to get certificate CRL/,
+ do {
+ open my $fh, '<', $delta_crl_as_complete_reasons_stderr;
+ <$fh>
+ }),
+ "Delta CRL with onlySomeReasons is not accepted as complete CRL");
+
# CAstore option
my $rootcertname = "root-cert";
my $rootcert = srctop_file(@certspath, "${rootcertname}.pem");