Commit 13db0cee52 for openssl.org

commit 13db0cee52a262e65227a5099a0e94f883a6c042
Author: Norbert Pocs <norbertp@openssl.org>
Date:   Thu Aug 27 13:59:30 2026 +0200

    Add test for CVE-2026-75805

    Signed-off-by: Norbert Pocs <norbertp@openssl.org>
    Reviewed-by: Richard Levitte <levitte@openssl.org>
    Reviewed-by: Andrew Dinh <andrewd@openssl.org>
    Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
    Merge-date: Tue Sep 29 11:00:50 2026

diff --git a/test/cmp_client_test.c b/test/cmp_client_test.c
index a4a2842cd6..f3333885d5 100644
--- a/test/cmp_client_test.c
+++ b/test/cmp_client_test.c
@@ -10,6 +10,7 @@
  */

 #include "helpers/cmp_testlib.h"
+#include "../crypto/crmf/crmf_local.h" /* for manipulating the CertId issuer */

 #include "cmp_mock_srv.h"

@@ -220,6 +221,97 @@ static int test_exec_RR_ses_receive_error(void)
     return result;
 }

+/*
+ * Create a CertId the issuer of which is not a directoryName, such that
+ * OSSL_CRMF_CERTID_get0_issuer() yields NULL for it, while
+ * OSSL_CRMF_CERTID_get0_serialNumber() yields the serial number as usual.
+ */
+static OSSL_CRMF_CERTID *certid_new_non_dirName_issuer(void)
+{
+    OSSL_CRMF_CERTID *cid = OSSL_CRMF_CERTID_new();
+    ASN1_IA5STRING *dns = ASN1_IA5STRING_new();
+
+    if (cid == NULL || dns == NULL)
+        goto err;
+    if (!ASN1_STRING_set1_string(dns, "server.example"))
+        goto err;
+    GENERAL_NAME_set0_value(cid->issuer, GEN_DNS, dns);
+    dns = NULL; /* ownership transferred to cid->issuer */
+    if (!ASN1_INTEGER_set(cid->serialNumber, 1))
+        goto err;
+    return cid;
+
+err:
+    ASN1_IA5STRING_free(dns);
+    OSSL_CRMF_CERTID_free(cid);
+    return NULL;
+}
+
+/*
+ * Transfer callback wrapping the mock server: add a CertId to the revCerts
+ * field of the RP, which the server side omits for an RR request derived from
+ * a PKCS#10 CSR because such a request contains no issuer and serial number.
+ */
+static OSSL_CMP_MSG *transfer_add_revCerts(OSSL_CMP_CTX *ctx,
+    const OSSL_CMP_MSG *req)
+{
+    OSSL_CMP_SRV_CTX *srv_ctx = OSSL_CMP_CTX_get_transfer_cb_arg(ctx);
+    OSSL_CMP_MSG *rp = ossl_cmp_mock_server_perform(ctx, req);
+    OSSL_CRMF_CERTID *cid;
+
+    if (rp == NULL || OSSL_CMP_MSG_get_bodytype(rp) != OSSL_CMP_PKIBODY_RP)
+        return rp;
+
+    if ((cid = certid_new_non_dirName_issuer()) == NULL)
+        goto err;
+    if (!sk_OSSL_CRMF_CERTID_push(rp->body->value.rp->revCerts, cid)) {
+        OSSL_CRMF_CERTID_free(cid);
+        goto err;
+    }
+    /* the body has been modified after the server protected the message */
+    if (!ossl_cmp_msg_protect(OSSL_CMP_SRV_CTX_get0_cmp_ctx(srv_ctx), rp))
+        goto err;
+    return rp;
+
+err:
+    OSSL_CMP_MSG_free(rp);
+    return NULL;
+}
+
+/*
+ * The certificate to be revoked is given by a PKCS#10 CSR, so the RR contains
+ * neither issuer nor serial number, yet the RP contains a CertId in revCerts.
+ * The client cannot compare the CertId with what it did not send and thus
+ * must not reject the response.
+ * The CertId issuer is not a directoryName, such that the issuer comparison
+ * compares NULL with NULL and succeeds, which makes the client go on
+ * comparing the serial numbers with the one it did not send being NULL.
+ */
+static int test_exec_RR_ses_p10CSR_revCerts(void)
+{
+    OSSL_CMP_CTX *ctx;
+    X509_REQ *csr = NULL;
+
+    SETUP_TEST_FIXTURE(CMP_SES_TEST_FIXTURE, set_up);
+    ctx = fixture->cmp_ctx;
+    fixture->expected = OSSL_CMP_PKISTATUS_accepted;
+    if (!TEST_ptr(csr = load_csr_der(pkcs10_f, libctx))
+        /* drop the reference cert such that the CSR is used instead */
+        || !TEST_true(OSSL_CMP_CTX_set1_oldCert(ctx, NULL))
+        || !TEST_true(OSSL_CMP_CTX_set1_p10CSR(ctx, csr))
+        /* no recipient can be derived from just a CSR */
+        || !TEST_true(OSSL_CMP_CTX_set1_recipient(ctx,
+            X509_get_subject_name(server_cert)))
+        || !TEST_true(OSSL_CMP_CTX_set_transfer_cb(ctx,
+            transfer_add_revCerts))) {
+        tear_down(fixture);
+        fixture = NULL;
+    }
+    X509_REQ_free(csr);
+    EXECUTE_TEST(execute_exec_RR_ses_test, tear_down);
+    return result;
+}
+
 static int test_exec_IR_ses(void)
 {
     SETUP_TEST_FIXTURE(CMP_SES_TEST_FIXTURE, set_up);
@@ -605,6 +697,7 @@ int setup_tests(void)
     ADD_TEST(test_exec_RR_ses_ok);
     ADD_TEST(test_exec_RR_ses_request_error);
     ADD_TEST(test_exec_RR_ses_receive_error);
+    ADD_TEST(test_exec_RR_ses_p10CSR_revCerts);
     ADD_TEST(test_exec_CR_ses_explicit_confirm);
     ADD_TEST(test_exec_CR_ses_implicit_confirm);
     ADD_TEST(test_exec_IR_ses);