Commit 355ea2ba25 for openssl.org
commit 355ea2ba25f45bebf41eb08a290ce31aa5478b8f
Author: 1seal <security@1seal.org>
Date: Tue Mar 17 10:14:32 2026 +0100
test: add regression tests for unauthorized OCSP response signers
extend test_tlsext_status_type() with a handshake that serves a
leaf-signed stapled OCSP response and verifies the connection fails
when X509_V_FLAG_OCSP_RESP_CHECK is enabled.
generalize ocsp_server_cb_single() to use configurable signer
cert/key instead of hardcoded paths so the same callback serves
both authorized and unauthorized signer test cases.
add a test_ocsp() subtest covering the -issuer CLI option with
an untrusted issuer hint.
Reviewed-by: David von Oheimb <david.von.oheimb@siemens.com>
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
MergeDate: Sat Mar 21 20:58:29 2026
(Merged from https://github.com/openssl/openssl/pull/30323)
diff --git a/test/recipes/80-test_ocsp.t b/test/recipes/80-test_ocsp.t
index 0539c79d56..3e12a0b23e 100644
--- a/test/recipes/80-test_ocsp.t
+++ b/test/recipes/80-test_ocsp.t
@@ -37,22 +37,24 @@ sub test_ocsp {
}
my $expected_exit = shift;
my $nochecks = shift;
+ my $opt_untrusted = shift // "-verify_other";
my $outputfile = basename($inputfile, '.ors') . '.dat';
run(app(["openssl", "base64", "-d",
"-in", catfile($ocspdir,$inputfile),
"-out", $outputfile]));
+ my @certopt = ($opt_untrusted, catfile($ocspdir, $untrusted));
with({ exit_checker => sub { return shift == $expected_exit; } },
sub { ok(run(app(["openssl", "ocsp", "-respin", $outputfile,
"-partial_chain", @check_time,
"-CAfile", catfile($ocspdir, $CAfile),
- "-verify_other", catfile($ocspdir, $untrusted),
+ @certopt,
"-no-CApath", "-no-CAstore",
$nochecks ? "-no_cert_checks" : ()])),
$title); });
}
-plan tests => 12;
+plan tests => 13;
subtest "=== VALID OCSP RESPONSES ===" => sub {
plan tests => 7;
@@ -230,6 +232,14 @@ subtest "=== OCSP API TESTS===" => sub {
"running ocspapitest");
};
+subtest "=== UNTRUSTED ISSUER HINTS ===" => sub {
+ plan tests => 1;
+
+ test_ocsp("NON-DELEGATED; invalid issuer via -issuer",
+ "ND1.ors", "ND1_Cross_Root.pem",
+ "ISIC_ND1_Issuer_ICA.pem", 1, 0, "-issuer");
+};
+
subtest "=== OCSP handling of identical input and output files ===" => sub {
plan tests => 5;
diff --git a/test/sslapitest.c b/test/sslapitest.c
index de60ed8ada..62d8dc252b 100644
--- a/test/sslapitest.c
+++ b/test/sslapitest.c
@@ -119,11 +119,14 @@ static int error_writing_log = 0;
#ifndef OPENSSL_NO_OCSP
static int ocsp_server_called = 0;
static int ocsp_client_called = 0;
+static int ocsp_verify_error = X509_V_OK;
#ifndef OSSL_NO_USABLE_TLS1_3
static int ocsp_verify_cb_called = 0;
#endif
static int cdummyarg = 1;
static X509 *ocspcert = NULL;
+static const char *ocsp_signer_key = "subinterCA.key";
+static const char *ocsp_signer_cert = "subinterCA.pem";
#endif
#define CLIENT_VERSION_LEN 2
@@ -1856,7 +1859,7 @@ static int test_cleanse_plaintext(void)
#ifndef OPENSSL_NO_OCSP
static OCSP_RESPONSE *create_ocsp_resp(X509 *ssl_cert, X509 *issuer, int status,
- char *signer_key_files, char *signer_cert_files)
+ const char *signer_key_files, const char *signer_cert_files)
{
ASN1_TIME *thisupd = X509_gmtime_adj(NULL, 0);
ASN1_TIME *nextupd = X509_time_adj_ex(NULL, 1, 0, NULL);
@@ -1929,7 +1932,8 @@ static int ocsp_server_cb_single(SSL *s, void *arg)
SSL_get0_chain_certs(s, &server_certs);
issuer = sk_X509_value(server_certs, 0);
- ocsp_resp = create_ocsp_resp(ssl_cert, issuer, V_OCSP_CERTSTATUS_GOOD, "subinterCA.key", "subinterCA.pem");
+ ocsp_resp = create_ocsp_resp(ssl_cert, issuer, V_OCSP_CERTSTATUS_GOOD,
+ ocsp_signer_key, ocsp_signer_cert);
if (!TEST_ptr(ocsp_resp))
return SSL_TLSEXT_ERR_ALERT_FATAL;
@@ -1967,6 +1971,13 @@ static int ocsp_client_cb_single(SSL *s, void *arg)
return 1;
}
+static int verify_cb_capture_error(int preverify_ok, X509_STORE_CTX *x509_ctx)
+{
+ if (!preverify_ok && ocsp_verify_error == X509_V_OK)
+ ocsp_verify_error = X509_STORE_CTX_get_error(x509_ctx);
+ return preverify_ok;
+}
+
static int test_tlsext_status_type(void)
{
SSL_CTX *cctx = NULL, *sctx = NULL;
@@ -2093,9 +2104,59 @@ static int test_tlsext_status_type(void)
|| !TEST_true(ocsp_server_called))
goto end;
+ /*
+ * Test that a stapled OCSP response signed by the leaf certificate
+ * (unauthorized signer) is rejected when X509_V_FLAG_OCSP_RESP_CHECK
+ * is enabled. Reuse the existing sctx/cctx, adding only the trust
+ * anchor, verify callback, and OCSP response check flag.
+ */
+ SSL_free(serverssl);
+ SSL_free(clientssl);
+ serverssl = clientssl = NULL;
+
+ ocsp_signer_key = "leaf.key";
+ ocsp_signer_cert = "leaf.pem";
+ ocsp_server_called = 0;
+ ocsp_verify_error = X509_V_OK;
+ cdummyarg = 1;
+
+ {
+ char *root = test_mk_file_path(certsdir, "rootCA.pem");
+
+ if (!TEST_ptr(root)
+ || !TEST_true(SSL_CTX_load_verify_locations(cctx, root, NULL))) {
+ OPENSSL_free(root);
+ goto end;
+ }
+ OPENSSL_free(root);
+ }
+ SSL_CTX_set_verify(cctx, SSL_VERIFY_PEER, verify_cb_capture_error);
+ {
+ X509_VERIFY_PARAM *vpm = X509_VERIFY_PARAM_new();
+
+ if (!TEST_ptr(vpm))
+ goto end;
+ X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_OCSP_RESP_CHECK);
+ if (!TEST_true(SSL_CTX_set1_param(cctx, vpm))) {
+ X509_VERIFY_PARAM_free(vpm);
+ goto end;
+ }
+ X509_VERIFY_PARAM_free(vpm);
+ }
+
+ if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl,
+ NULL, NULL))
+ || !TEST_false(create_ssl_connection(serverssl, clientssl,
+ SSL_ERROR_SSL))
+ || !TEST_int_eq(ocsp_server_called, 1)
+ || !TEST_int_eq(ocsp_verify_error, X509_V_ERR_OCSP_VERIFY_FAILED))
+ goto end;
+
testresult = 1;
end:
+ ocsp_signer_key = "subinterCA.key";
+ ocsp_signer_cert = "subinterCA.pem";
SSL_free(serverssl);
SSL_free(clientssl);
SSL_CTX_free(sctx);