Commit 52ee7fdf27 for openssl.org

commit 52ee7fdf278c2316f1ddd94526f8730e014dd58f
Author: Alexandr Nedvedicky <sashan@openssl.org>
Date:   Thu Jul 30 16:02:57 2026 +0200

    SSL_dup() should copy additional members

    currently those members are missing to be copied in SSL_dup(3ossl)
       - server_cert_type
       - client_cert_type
       - ct_validation_callback
       - retsc->ext.status_type (typically needed for OCSP)

    All those missing members are usually set before handshake starts,
    so SSL_dup() needs to copy them so behavior of duplicated SSL
    object is consistent with its original.

    Reviewed-by: Neil Horman <nhorman@openssl.org>
    Reviewed-by: Andrew Dinh <andrewd@openssl.org>
    Merge-date: Wed Aug 19 14:26:25 2026
    Merged-from: https://github.com/openssl/openssl/pull/32122

diff --git a/doc/man3/SSL_new.pod b/doc/man3/SSL_new.pod
index 829229461d..64d726e10e 100644
--- a/doc/man3/SSL_new.pod
+++ b/doc/man3/SSL_new.pod
@@ -96,6 +96,14 @@ SSL_set0_client_CA_list() or similar functions

 =item any Encrypted Client Hello (ECH) settings (see L<SSL_set1_echstore(3)>).

+=item type of server certificate set via L<SSL_set1_server_cert_type(3)>.
+
+=item type of client certificate set via L<SSL_set1_client_cert_type(3)>.
+
+=item certificate transparency validation callback set via L<SSL_set_ct_validation_callback(3)>,
+
+=item OCSP validation set via L<SSL_set_tlsext_status_type(3)>.
+
 =back

 SSL_dup() is not supported on QUIC SSL objects and returns NULL if called on
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 362c720bcf..f48a5d9004 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -5494,6 +5494,31 @@ SSL *SSL_dup(SSL *s)
         || !dup_ca_names(&retsc->client_ca_names, sc->client_ca_names))
         goto err;

+    if (sc->server_cert_type != NULL) {
+        OPENSSL_free(retsc->server_cert_type);
+        retsc->server_cert_type = OPENSSL_memdup(sc->server_cert_type,
+            sc->server_cert_type_len);
+        if (retsc->server_cert_type == NULL)
+            goto err;
+        retsc->server_cert_type_len = sc->server_cert_type_len;
+    }
+
+    if (sc->client_cert_type != NULL) {
+        OPENSSL_free(retsc->client_cert_type);
+        retsc->client_cert_type = OPENSSL_memdup(sc->client_cert_type,
+            sc->client_cert_type_len);
+        if (retsc->client_cert_type == NULL)
+            goto err;
+        retsc->client_cert_type_len = sc->client_cert_type_len;
+    }
+
+#ifndef OPENSSL_NO_CT
+    retsc->ct_validation_callback = sc->ct_validation_callback;
+    retsc->ct_validation_callback_arg = sc->ct_validation_callback_arg;
+#endif
+
+    retsc->ext.status_type = sc->ext.status_type;
+
     return ret;

 err:
diff --git a/test/sslapitest.c b/test/sslapitest.c
index b6081be702..c7c722c08a 100644
--- a/test/sslapitest.c
+++ b/test/sslapitest.c
@@ -11670,12 +11670,29 @@ end:
 #endif

 #ifndef OPENSSL_NO_TLS1_2
+
+#define CERT_TYPE_C "\x0" /* TLSEXT_cert_type_x509 */
+#define CERT_TYPE_S "\x2" /* TLSEXT_cert_type_rpk */
+
+#ifndef OPENSSL_NO_CT
+#define CB_ARG "callback arg"
+
+/* ARGSUSED */
+static int validation_cbk(const CT_POLICY_EVAL_CTX *ctx,
+    const STACK_OF(SCT) *scts, void *arg)
+{
+    return 1;
+}
+#endif
+
 static int test_ssl_dup(void)
 {
     SSL_CTX *cctx = NULL, *sctx = NULL;
     SSL *clientssl = NULL, *serverssl = NULL, *client2ssl = NULL;
     int testresult = 0;
     BIO *rbio = NULL, *wbio = NULL;
+    unsigned char *ctype;
+    size_t ctype_len;

     if (!TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(),
             TLS_client_method(),
@@ -11692,6 +11709,27 @@ static int test_ssl_dup(void)
         || !TEST_true(SSL_set_max_proto_version(clientssl, TLS1_2_VERSION)))
         goto end;

+    if (!TEST_true(
+            SSL_set1_client_cert_type(clientssl,
+                (const unsigned char *)CERT_TYPE_C, sizeof(CERT_TYPE_C) - 1)))
+        goto end;
+
+    if (!TEST_true(
+            SSL_set1_server_cert_type(clientssl,
+                (const unsigned char *)CERT_TYPE_S, sizeof(CERT_TYPE_S) - 1)))
+        goto end;
+
+#ifndef OPENSSL_NO_CT
+    if (!TEST_true(SSL_set_ct_validation_callback(clientssl, validation_cbk, CB_ARG)))
+        goto end;
+#endif
+
+#ifndef OPENSSL_NO_OCSP
+    if (!TEST_true(
+            SSL_set_tlsext_status_type(clientssl, TLSEXT_STATUSTYPE_ocsp)))
+        goto end;
+#endif
+
     client2ssl = SSL_dup(clientssl);
     rbio = SSL_get_rbio(clientssl);
     if (!TEST_ptr(rbio)
@@ -11719,6 +11757,28 @@ static int test_ssl_dup(void)
     if (!TEST_true(create_ssl_connection(serverssl, client2ssl, SSL_ERROR_NONE)))
         goto end;

+    if (!TEST_true(SSL_get0_client_cert_type(client2ssl, &ctype, &ctype_len)))
+        goto end;
+
+    if (!TEST_mem_eq(ctype, ctype_len, CERT_TYPE_C, sizeof(CERT_TYPE_C) - 1))
+        goto end;
+
+    if (!TEST_true(SSL_get0_server_cert_type(client2ssl, &ctype, &ctype_len)))
+        goto end;
+
+    if (!TEST_mem_eq(ctype, ctype_len, CERT_TYPE_S, sizeof(CERT_TYPE_S) - 1))
+        goto end;
+
+#ifndef OPENSSL_NO_CT
+    if (!TEST_true(SSL_ct_is_enabled(client2ssl)))
+        goto end;
+#endif
+
+#ifndef OPENSSL_NO_OCSP
+    if (!TEST_long_eq(SSL_get_tlsext_status_type(client2ssl), TLSEXT_STATUSTYPE_ocsp))
+        goto end;
+#endif
+
     SSL_free(clientssl);
     clientssl = SSL_dup(client2ssl);
     if (!TEST_ptr(clientssl)