Package: release.debian.org
Severity: normal
Tags: trixie
X-Debbugs-Cc: [email protected], [email protected]
Control: affects -1 + src:pyopenssl
User: [email protected]
Usertags: pu
* CVE-2026-27448: Unhandled exceptions in set_tlsext_servername_callback
callbacks did not cancel connections
* CVE-2026-27459: Buffer overflow in DTLS cookie callback
diffstat for pyopenssl-25.0.0 pyopenssl-25.0.0
changelog | 9
patches/0001-Handle-exceptions-in-set_tlsext_servername_callback-.patch | 107
++++++++++
patches/0002-Fix-buffer-overflow-in-DTLS-cookie-generation-callba.patch | 91
++++++++
patches/series | 2
4 files changed, 209 insertions(+)
diff -Nru pyopenssl-25.0.0/debian/changelog pyopenssl-25.0.0/debian/changelog
--- pyopenssl-25.0.0/debian/changelog 2025-01-17 20:22:27.000000000 +0200
+++ pyopenssl-25.0.0/debian/changelog 2026-06-22 19:13:17.000000000 +0300
@@ -1,3 +1,12 @@
+pyopenssl (25.0.0-1+deb13u1) trixie; urgency=medium
+
+ * Non-maintainer upload.
+ * CVE-2026-27448: Unhandled exceptions in set_tlsext_servername_callback
+ callbacks did not cancel connections
+ * CVE-2026-27459: Buffer overflow in DTLS cookie callback
+
+ -- Adrian Bunk <[email protected]> Mon, 22 Jun 2026 19:13:17 +0300
+
pyopenssl (25.0.0-1) unstable; urgency=medium
* New upstream version.
diff -Nru
pyopenssl-25.0.0/debian/patches/0001-Handle-exceptions-in-set_tlsext_servername_callback-.patch
pyopenssl-25.0.0/debian/patches/0001-Handle-exceptions-in-set_tlsext_servername_callback-.patch
---
pyopenssl-25.0.0/debian/patches/0001-Handle-exceptions-in-set_tlsext_servername_callback-.patch
1970-01-01 02:00:00.000000000 +0200
+++
pyopenssl-25.0.0/debian/patches/0001-Handle-exceptions-in-set_tlsext_servername_callback-.patch
2026-06-22 19:11:08.000000000 +0300
@@ -0,0 +1,107 @@
+From df3c6da11667645e7ca886a7f4fe7f2d9d8d80d7 Mon Sep 17 00:00:00 2001
+From: Alex Gaynor <[email protected]>
+Date: Mon, 16 Feb 2026 21:04:37 -0500
+Subject: Handle exceptions in set_tlsext_servername_callback callbacks (#1478)
+
+When the servername callback raises an exception, call sys.excepthook
+with the exception info and return SSL_TLSEXT_ERR_ALERT_FATAL to abort
+the handshake. Previously, exceptions would propagate uncaught through
+the CFFI callback boundary.
+
+https://claude.ai/code/session_01P7y1XmWkdtC5UcmZwGDvGi
+
+Co-authored-by: Claude <[email protected]>
+---
+ src/OpenSSL/SSL.py | 7 ++++++-
+ tests/test_ssl.py | 50 ++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 56 insertions(+), 1 deletion(-)
+
+diff --git a/src/OpenSSL/SSL.py b/src/OpenSSL/SSL.py
+index bc9c188..1dbd1c8 100644
+--- a/src/OpenSSL/SSL.py
++++ b/src/OpenSSL/SSL.py
+@@ -2,6 +2,7 @@ from __future__ import annotations
+
+ import os
+ import socket
++import sys
+ import typing
+ import warnings
+ from collections.abc import Sequence
+@@ -1686,7 +1687,11 @@ class Context:
+
+ @wraps(callback)
+ def wrapper(ssl, alert, arg): # type: ignore[no-untyped-def]
+- callback(Connection._reverse_mapping[ssl])
++ try:
++ callback(Connection._reverse_mapping[ssl])
++ except Exception:
++ sys.excepthook(*sys.exc_info())
++ return _lib.SSL_TLSEXT_ERR_ALERT_FATAL
+ return 0
+
+ self._tlsext_servername_callback = _ffi.callback(
+diff --git a/tests/test_ssl.py b/tests/test_ssl.py
+index 21d30cf..0f0dad1 100644
+--- a/tests/test_ssl.py
++++ b/tests/test_ssl.py
+@@ -1936,6 +1936,56 @@ class TestServerNameCallback:
+
+ assert args == [(server, b"foo1.example.com")]
+
++ def test_servername_callback_exception(
++ self, monkeypatch: pytest.MonkeyPatch
++ ) -> None:
++ """
++ When the callback passed to `Context.set_tlsext_servername_callback`
++ raises an exception, ``sys.excepthook`` is called with the exception
++ and the handshake fails with an ``Error``.
++ """
++ exc = TypeError("server name callback failed")
++
++ def servername(conn: Connection) -> None:
++ raise exc
++
++ excepthook_calls: list[
++ tuple[type[BaseException], BaseException, object]
++ ] = []
++
++ def custom_excepthook(
++ exc_type: type[BaseException],
++ exc_value: BaseException,
++ exc_tb: object,
++ ) -> None:
++ excepthook_calls.append((exc_type, exc_value, exc_tb))
++
++ context = Context(SSLv23_METHOD)
++ context.set_tlsext_servername_callback(servername)
++
++ # Necessary to actually accept the connection
++ context.use_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem))
++ context.use_certificate(
++ load_certificate(FILETYPE_PEM, server_cert_pem)
++ )
++
++ # Do a little connection to trigger the logic
++ server = Connection(context, None)
++ server.set_accept_state()
++
++ client = Connection(Context(SSLv23_METHOD), None)
++ client.set_connect_state()
++ client.set_tlsext_host_name(b"foo1.example.com")
++
++ monkeypatch.setattr(sys, "excepthook", custom_excepthook)
++ with pytest.raises(Error):
++ interact_in_memory(server, client)
++
++ assert len(excepthook_calls) == 1
++ assert excepthook_calls[0][0] is TypeError
++ assert excepthook_calls[0][1] is exc
++ assert excepthook_calls[0][2] is not None
++
+
+ class TestApplicationLayerProtoNegotiation:
+ """
+--
+2.47.3
+
diff -Nru
pyopenssl-25.0.0/debian/patches/0002-Fix-buffer-overflow-in-DTLS-cookie-generation-callba.patch
pyopenssl-25.0.0/debian/patches/0002-Fix-buffer-overflow-in-DTLS-cookie-generation-callba.patch
---
pyopenssl-25.0.0/debian/patches/0002-Fix-buffer-overflow-in-DTLS-cookie-generation-callba.patch
1970-01-01 02:00:00.000000000 +0200
+++
pyopenssl-25.0.0/debian/patches/0002-Fix-buffer-overflow-in-DTLS-cookie-generation-callba.patch
2026-06-22 19:11:08.000000000 +0300
@@ -0,0 +1,91 @@
+From 434d56816d36adb05c87a6fea0b1ee3da1cea8bf Mon Sep 17 00:00:00 2001
+From: Alex Gaynor <[email protected]>
+Date: Wed, 18 Feb 2026 07:46:15 -0500
+Subject: Fix buffer overflow in DTLS cookie generation callback (#1479)
+
+The cookie generate callback copied user-returned bytes into a
+fixed-size native buffer without enforcing a maximum length. A
+callback returning more than DTLS1_COOKIE_LENGTH bytes would overflow
+the OpenSSL-provided buffer, corrupting adjacent memory.
+
+Co-authored-by: Claude Opus 4.6 <[email protected]>
+---
+ src/OpenSSL/SSL.py | 7 +++++++
+ tests/test_ssl.py | 38 ++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 45 insertions(+)
+
+diff --git a/src/OpenSSL/SSL.py b/src/OpenSSL/SSL.py
+index 1dbd1c8..bccf920 100644
+--- a/src/OpenSSL/SSL.py
++++ b/src/OpenSSL/SSL.py
+@@ -716,11 +716,18 @@ class
_CookieGenerateCallbackHelper(_CallbackExceptionHelper):
+ def __init__(self, callback: _CookieGenerateCallback) -> None:
+ _CallbackExceptionHelper.__init__(self)
+
++ max_cookie_len = getattr(_lib, "DTLS1_COOKIE_LENGTH", 255)
++
+ @wraps(callback)
+ def wrapper(ssl, out, outlen): # type: ignore[no-untyped-def]
+ try:
+ conn = Connection._reverse_mapping[ssl]
+ cookie = callback(conn)
++ if len(cookie) > max_cookie_len:
++ raise ValueError(
++ f"Cookie too long (got {len(cookie)} bytes, "
++ f"max {max_cookie_len})"
++ )
+ out[0 : len(cookie)] = cookie
+ outlen[0] = len(cookie)
+ return 1
+diff --git a/tests/test_ssl.py b/tests/test_ssl.py
+index 0f0dad1..41b37be 100644
+--- a/tests/test_ssl.py
++++ b/tests/test_ssl.py
+@@ -4727,6 +4727,44 @@ class TestDTLS:
+ def test_it_works_with_srtp(self) -> None:
+ self._test_handshake_and_data(srtp_profile=b"SRTP_AES128_CM_SHA1_80")
+
++ def test_cookie_generate_too_long(self) -> None:
++ s_ctx = Context(DTLS_METHOD)
++
++ def generate_cookie(ssl: Connection) -> bytes:
++ return b"\x00" * 256
++
++ def verify_cookie(ssl: Connection, cookie: bytes) -> bool:
++ return True
++
++ s_ctx.set_cookie_generate_callback(generate_cookie)
++ s_ctx.set_cookie_verify_callback(verify_cookie)
++ s_ctx.use_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem))
++ s_ctx.use_certificate(load_certificate(FILETYPE_PEM, server_cert_pem))
++ s_ctx.set_options(OP_NO_QUERY_MTU)
++ s = Connection(s_ctx)
++ s.set_accept_state()
++
++ c_ctx = Context(DTLS_METHOD)
++ c_ctx.set_options(OP_NO_QUERY_MTU)
++ c = Connection(c_ctx)
++ c.set_connect_state()
++
++ c.set_ciphertext_mtu(1500)
++ s.set_ciphertext_mtu(1500)
++
++ # Client sends ClientHello
++ try:
++ c.do_handshake()
++ except SSL.WantReadError:
++ pass
++ chunk = c.bio_read(self.LARGE_BUFFER)
++ s.bio_write(chunk)
++
++ # Server tries DTLSv1_listen, which triggers cookie generation.
++ # The oversized cookie should raise ValueError.
++ with pytest.raises(ValueError, match="Cookie too long"):
++ s.DTLSv1_listen()
++
+ def test_timeout(self, monkeypatch: pytest.MonkeyPatch) -> None:
+ c_ctx = Context(DTLS_METHOD)
+ c = Connection(c_ctx)
+--
+2.47.3
+
diff -Nru pyopenssl-25.0.0/debian/patches/series
pyopenssl-25.0.0/debian/patches/series
--- pyopenssl-25.0.0/debian/patches/series 2025-01-17 20:22:27.000000000
+0200
+++ pyopenssl-25.0.0/debian/patches/series 2026-06-22 19:13:16.000000000
+0300
@@ -1,2 +1,4 @@
0001-disable-test_set_default_verify_paths-since-it-tries.patch
0003-Fix-running-tests-on-32-bit-time_t-platforms.patch
+0001-Handle-exceptions-in-set_tlsext_servername_callback-.patch
+0002-Fix-buffer-overflow-in-DTLS-cookie-generation-callba.patch