Commit dc5cd1054a for openssl.org
commit dc5cd1054aa38cd507fc3fc9593ebda7d36ca3dc
Author: Matt Caswell <matt@openssl.foundation>
Date: Mon Nov 2 12:08:31 2026 +0000
Deprecate SSL_copy_session_id() and BIO_ssl_copy_session_id()
Besides the session, SSL_copy_session_id() gives the target the SSL
method, session ID context and certificate configuration of the source.
Only SSL_dup() needs that, and it now uses an internal function.
Applications resuming a session should use SSL_set_session().
Also document SSL_copy_session_id().
Assisted-by: Claude Code:claude-opus-5-5
Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
Merge-date: Fri Nov 6 12:55:14 2026
Merged-from: https://github.com/openssl/openssl/pull/33489
diff --git a/CHANGES.md b/CHANGES.md
index 4bdb6fc978..09ffde8bca 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -33,6 +33,12 @@ OpenSSL 4.2
### Changes between 4.1 and 4.2 [xx XXX xxxx]
+ * Deprecated `SSL_copy_session_id()` and `BIO_ssl_copy_session_id()`.
+ Use `SSL_set_session()` to resume a session on a new connection, or
+ `SSL_dup()` to copy an SSL object.
+
+ *Matt Caswell*
+
* Added `BIO_dgram_pair_set0_pcap_file()` which intercepts and writes UDP
datagrams exchanged of over `BIO_dgram_pair` bio memory object to file
in libpcap format. To enable `BIO_dgram_pair_set0_pcap_file()` the build
diff --git a/doc/build.info b/doc/build.info
index 0db3744027..4926e140a2 100644
--- a/doc/build.info
+++ b/doc/build.info
@@ -2579,6 +2579,10 @@ DEPEND[html/man3/SSL_connect.html]=man3/SSL_connect.pod
GENERATE[html/man3/SSL_connect.html]=man3/SSL_connect.pod
DEPEND[man/man3/SSL_connect.3]=man3/SSL_connect.pod
GENERATE[man/man3/SSL_connect.3]=man3/SSL_connect.pod
+DEPEND[html/man3/SSL_copy_session_id.html]=man3/SSL_copy_session_id.pod
+GENERATE[html/man3/SSL_copy_session_id.html]=man3/SSL_copy_session_id.pod
+DEPEND[man/man3/SSL_copy_session_id.3]=man3/SSL_copy_session_id.pod
+GENERATE[man/man3/SSL_copy_session_id.3]=man3/SSL_copy_session_id.pod
DEPEND[html/man3/SSL_do_handshake.html]=man3/SSL_do_handshake.pod
GENERATE[html/man3/SSL_do_handshake.html]=man3/SSL_do_handshake.pod
DEPEND[man/man3/SSL_do_handshake.3]=man3/SSL_do_handshake.pod
@@ -3732,6 +3736,7 @@ html/man3/SSL_alloc_buffers.html \
html/man3/SSL_check_chain.html \
html/man3/SSL_clear.html \
html/man3/SSL_connect.html \
+html/man3/SSL_copy_session_id.html \
html/man3/SSL_do_handshake.html \
html/man3/SSL_export_keying_material.html \
html/man3/SSL_extension_supported.html \
@@ -4416,6 +4421,7 @@ man/man3/SSL_alloc_buffers.3 \
man/man3/SSL_check_chain.3 \
man/man3/SSL_clear.3 \
man/man3/SSL_connect.3 \
+man/man3/SSL_copy_session_id.3 \
man/man3/SSL_do_handshake.3 \
man/man3/SSL_export_keying_material.3 \
man/man3/SSL_extension_supported.3 \
diff --git a/doc/man3/BIO_f_ssl.pod b/doc/man3/BIO_f_ssl.pod
index 051a36efb9..fad31383d8 100644
--- a/doc/man3/BIO_f_ssl.pod
+++ b/doc/man3/BIO_f_ssl.pod
@@ -28,11 +28,16 @@ BIO_ssl_shutdown - SSL BIO
BIO *BIO_new_ssl(SSL_CTX *ctx, int client);
BIO *BIO_new_ssl_connect(SSL_CTX *ctx);
BIO *BIO_new_buffer_ssl_connect(SSL_CTX *ctx);
- int BIO_ssl_copy_session_id(BIO *to, BIO *from);
void BIO_ssl_shutdown(BIO *bio);
long BIO_do_handshake(BIO *b);
+The following function has been deprecated since OpenSSL 4.2, and can be
+hidden entirely by defining B<OPENSSL_API_COMPAT> with a suitable version value,
+see L<openssl_user_macros(7)>:
+
+ int BIO_ssl_copy_session_id(BIO *to, BIO *from);
+
=head1 DESCRIPTION
BIO_f_ssl() returns the SSL BIO method. This is a filter BIO which
@@ -88,7 +93,7 @@ of a buffering BIO, an SSL BIO (using B<ctx>), and a connect BIO.
BIO_ssl_copy_session_id() copies an SSL session id between
BIO chains B<from> and B<to>. It does this by locating the
SSL BIOs in each chain and calling SSL_copy_session_id() on
-the internal SSL pointer.
+the internal SSL pointer. It is deprecated; see L<SSL_copy_session_id(3)>.
BIO_ssl_shutdown() closes down an SSL connection on BIO
chain B<bio>. It does this by locating the SSL BIO in the
@@ -301,6 +306,8 @@ explicitly being popped (e.g. a pop higher up the chain). Applications which
included workarounds for this bug (e.g. freeing BIOs more than once) should
be modified to handle this fix or they may free up an already freed BIO.
+BIO_ssl_copy_session_id() was deprecated in OpenSSL 4.2.
+
=head1 COPYRIGHT
Copyright 2000-2026 The OpenSSL Project Authors. All Rights Reserved.
diff --git a/doc/man3/SSL_copy_session_id.pod b/doc/man3/SSL_copy_session_id.pod
new file mode 100644
index 0000000000..6e5d458203
--- /dev/null
+++ b/doc/man3/SSL_copy_session_id.pod
@@ -0,0 +1,55 @@
+=pod
+
+=head1 NAME
+
+SSL_copy_session_id - copy the session and configuration of one SSL object to another
+
+=head1 SYNOPSIS
+
+ #include <openssl/ssl.h>
+
+The following function has been deprecated since OpenSSL 4.2, and can be
+hidden entirely by defining B<OPENSSL_API_COMPAT> with a suitable version value,
+see L<openssl_user_macros(7)>:
+
+ int SSL_copy_session_id(SSL *to, const SSL *from);
+
+=head1 DESCRIPTION
+
+SSL_copy_session_id() sets the session of I<to> to the session of I<from>, as
+L<SSL_set_session(3)> would. It also gives I<to> the SSL method, session ID
+context and certificate configuration of I<from>. The two objects share the
+certificate configuration, so later changes made to it through either object
+affect both.
+
+I<to> and I<from> must have been created from the same B<SSL_CTX>, or from
+B<SSL_CTX> objects with the same providers loaded. Otherwise
+SSL_copy_session_id() may fail.
+
+=head1 RETURN VALUES
+
+SSL_copy_session_id() returns 1 on success and 0 on failure.
+
+=head1 NOTES
+
+This function is deprecated. To resume a session on a new connection, use
+L<SSL_set_session(3)>. To create a copy of an SSL object, use L<SSL_dup(3)>.
+
+=head1 SEE ALSO
+
+L<ssl(7)>, L<SSL_set_session(3)>, L<SSL_dup(3)>, L<BIO_f_ssl(3)>
+
+=head1 HISTORY
+
+SSL_copy_session_id() was deprecated in OpenSSL 4.2.
+
+=head1 COPYRIGHT
+
+Copyright 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
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/include/openssl/ssl.h.in b/include/openssl/ssl.h.in
index bdf5d201c9..9eb71eaa92 100644
--- a/include/openssl/ssl.h.in
+++ b/include/openssl/ssl.h.in
@@ -1609,7 +1609,13 @@ __owur const BIO_METHOD *BIO_f_ssl(void);
__owur BIO *BIO_new_ssl(SSL_CTX *ctx, int client);
__owur BIO *BIO_new_ssl_connect(SSL_CTX *ctx);
__owur BIO *BIO_new_buffer_ssl_connect(SSL_CTX *ctx);
-__owur int BIO_ssl_copy_session_id(BIO *to, BIO *from);
+#ifndef OPENSSL_NO_DEPRECATED_4_2
+/**
+ * @brief Copy the session and configuration between SSL BIO chains.
+ * @see BIO_ssl_copy_session_id(3)
+ */
+OSSL_DEPRECATEDIN_4_2 __owur int BIO_ssl_copy_session_id(BIO *to, BIO *from);
+#endif
void BIO_ssl_shutdown(BIO *ssl_bio);
__owur int SSL_CTX_set_cipher_list(SSL_CTX *, const char *str);
@@ -1773,7 +1779,13 @@ void SSL_SESSION_get0_ticket(const SSL_SESSION *s, const unsigned char **tick,
__owur uint32_t SSL_SESSION_get_max_early_data(const SSL_SESSION *s);
__owur int SSL_SESSION_set_max_early_data(SSL_SESSION *s,
uint32_t max_early_data);
-__owur int SSL_copy_session_id(SSL *to, const SSL *from);
+#ifndef OPENSSL_NO_DEPRECATED_4_2
+/**
+ * @brief Copy the session and configuration of one SSL object to another.
+ * @see SSL_copy_session_id(3)
+ */
+OSSL_DEPRECATEDIN_4_2 __owur int SSL_copy_session_id(SSL *to, const SSL *from);
+#endif
__owur X509 *SSL_SESSION_get0_peer(SSL_SESSION *s);
__owur int SSL_SESSION_set1_id_context(SSL_SESSION *s,
const unsigned char *sid_ctx,
diff --git a/ssl/bio_ssl.c b/ssl/bio_ssl.c
index ce58bce778..eb3162b30d 100644
--- a/ssl/bio_ssl.c
+++ b/ssl/bio_ssl.c
@@ -527,6 +527,7 @@ BIO *BIO_new_ssl(SSL_CTX *ctx, int client)
return ret;
}
+#ifndef OPENSSL_NO_DEPRECATED_4_2
int BIO_ssl_copy_session_id(BIO *t, BIO *f)
{
BIO_SSL *tdata, *fdata;
@@ -538,10 +539,11 @@ int BIO_ssl_copy_session_id(BIO *t, BIO *f)
fdata = BIO_get_data(f);
if ((tdata->ssl == NULL) || (fdata->ssl == NULL))
return 0;
- if (!SSL_copy_session_id(tdata->ssl, (fdata->ssl)))
+ if (!ossl_ssl_copy_session_id(tdata->ssl, (fdata->ssl)))
return 0;
return 1;
}
+#endif
void BIO_ssl_shutdown(BIO *b)
{
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index de0309d539..77bc5d7251 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -2213,7 +2213,7 @@ STACK_OF(X509) *SSL_get_peer_cert_chain(const SSL *s)
* Now in theory, since the calling process own 't' it should be safe to
* modify. We need to be able to read f without being hassled
*/
-int SSL_copy_session_id(SSL *t, const SSL *f)
+int ossl_ssl_copy_session_id(SSL *t, const SSL *f)
{
int i;
/* TODO(QUIC FUTURE): Not allowed for QUIC currently. */
@@ -2259,6 +2259,13 @@ int SSL_copy_session_id(SSL *t, const SSL *f)
return 1;
}
+#ifndef OPENSSL_NO_DEPRECATED_4_2
+int SSL_copy_session_id(SSL *t, const SSL *f)
+{
+ return ossl_ssl_copy_session_id(t, f);
+}
+#endif
+
/* Fix this so it checks all the valid key/cert options */
int SSL_CTX_check_private_key(const SSL_CTX *ctx)
{
@@ -5730,7 +5737,7 @@ SSL *SSL_dup(SSL *s)
* Arranges to share the same session via up_ref. This "copies"
* session-id, SSL_METHOD, sid_ctx, and 'cert'
*/
- if (!SSL_copy_session_id(ret, s))
+ if (!ossl_ssl_copy_session_id(ret, s))
goto err;
} else {
/*
diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h
index 28bd4a6df5..5036f86931 100644
--- a/ssl/ssl_local.h
+++ b/ssl/ssl_local.h
@@ -2919,6 +2919,17 @@ __owur SSL *ossl_ssl_connection_new_int(SSL_CTX *ctx, SSL *user_ssl,
__owur SSL *ossl_ssl_connection_new(SSL_CTX *ctx);
void ossl_ssl_connection_free(SSL *ssl);
__owur int ossl_ssl_connection_reset(SSL *ssl);
+/**
+ * @brief Give @p t the session, method, session ID context and CERT of @p f.
+ *
+ * The CERT is shared, not copied. Fails if @p f's CERT is sized for a
+ * different number of provider sigalgs than @p t's SSL_CTX.
+ *
+ * @param t The SSL object to copy to.
+ * @param f The SSL object to copy from.
+ * @return 1 on success, 0 on failure.
+ */
+__owur int ossl_ssl_copy_session_id(SSL *t, const SSL *f);
__owur int ssl_read_internal(SSL *s, void *buf, size_t num, size_t *readbytes);
__owur int ssl_write_internal(SSL *s, const void *buf, size_t num,
diff --git a/test/sslapitest.c b/test/sslapitest.c
index 933d58fbcb..37d0f36141 100644
--- a/test/sslapitest.c
+++ b/test/sslapitest.c
@@ -15347,6 +15347,7 @@ end:
}
#endif /* OPENSSL_NO_TLS1_2 */
+#ifndef OPENSSL_NO_DEPRECATED_4_2
/*
* SSL_copy_session_id() makes |t| share |f|'s CERT, but |t| keeps its own
* SSL_CTX. It must refuse a CERT sized for a different number of provider
@@ -15388,6 +15389,7 @@ end:
return testresult;
}
+#endif
/*
* The ClientHello raw extension array is sized when the ClientHello is
@@ -19787,7 +19789,9 @@ int setup_tests(void)
ADD_ALL_TESTS(test_sigalg_ctx_switch, 4);
ADD_ALL_TESTS(test_ctx_switch_more_custom_exts, 2);
ADD_ALL_TESTS(test_sigalg_ctx_switch_cert_cb, 2);
+#ifndef OPENSSL_NO_DEPRECATED_4_2
ADD_TEST(test_copy_session_id_ctx_mismatch);
+#endif
#ifndef OPENSSL_NO_TLS1_2
ADD_TEST(test_sigalg_ctx_switch_reneg);
#endif
diff --git a/util/libssl.num b/util/libssl.num
index 9b24054a7c..c81feb263e 100644
--- a/util/libssl.num
+++ b/util/libssl.num
@@ -103,7 +103,7 @@ BIO_f_ssl 102 4_0_0 EXIST::FUNCTION:
BIO_new_ssl 103 4_0_0 EXIST::FUNCTION:
BIO_new_ssl_connect 104 4_0_0 EXIST::FUNCTION:
BIO_new_buffer_ssl_connect 105 4_0_0 EXIST::FUNCTION:
-BIO_ssl_copy_session_id 106 4_0_0 EXIST::FUNCTION:
+BIO_ssl_copy_session_id 106 4_0_0 EXIST::FUNCTION:DEPRECATEDIN_4_2
BIO_ssl_shutdown 107 4_0_0 EXIST::FUNCTION:
SSL_CTX_set_cipher_list 108 4_0_0 EXIST::FUNCTION:
SSL_CTX_new 109 4_0_0 EXIST::FUNCTION:
@@ -204,7 +204,7 @@ SSL_SESSION_get_ticket_lifetime_hint 203 4_0_0 EXIST::FUNCTION:
SSL_SESSION_get0_ticket 204 4_0_0 EXIST::FUNCTION:
SSL_SESSION_get_max_early_data 205 4_0_0 EXIST::FUNCTION:
SSL_SESSION_set_max_early_data 206 4_0_0 EXIST::FUNCTION:
-SSL_copy_session_id 207 4_0_0 EXIST::FUNCTION:
+SSL_copy_session_id 207 4_0_0 EXIST::FUNCTION:DEPRECATEDIN_4_2
SSL_SESSION_get0_peer 208 4_0_0 EXIST::FUNCTION:
SSL_SESSION_set1_id_context 209 4_0_0 EXIST::FUNCTION:
SSL_SESSION_set1_id 210 4_0_0 EXIST::FUNCTION:
diff --git a/util/missingssl.txt b/util/missingssl.txt
index 0ab346fe57..83577abd2e 100644
--- a/util/missingssl.txt
+++ b/util/missingssl.txt
@@ -15,7 +15,6 @@ SSL_SRP_CTX_free(3)
SSL_SRP_CTX_init(3)
SSL_add_ssl_module(3)
SSL_certs_clear(3)
-SSL_copy_session_id(3)
SSL_dup_CA_list(3)
SSL_get0_dane(3)
SSL_get_current_compression(3)