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);