From 57f09bb4bb051d3bc2a1abd36e9525313d5cd408 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Wed, 18 Feb 2026 07:46:15 -0500 Subject: [PATCH] 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 Upstream-Status: Backport [https://github.com/pyca/pyopenssl/commit/57f09bb4bb051d3bc2a1abd36e9525313d5cd408] CVE: CVE-2026-27459 Signed-off-by: Vijay Anusuri --- CHANGELOG.rst | 1 + src/OpenSSL/SSL.py | 7 +++++++ tests/test_ssl.py | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5b6d523..13d8abd 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -20,6 +20,7 @@ Deprecations: Changes: ^^^^^^^^ +- Properly raise an error if a DTLS cookie callback returned a cookie longer than ``DTLS1_COOKIE_LENGTH`` bytes. Previously this would result in a buffer-overflow. - ``Context.set_tlsext_servername_callback`` now handles exceptions raised in the callback by calling ``sys.excepthook`` and returning a fatal TLS alert. Previously, exceptions were silently swallowed and the handshake would proceed as if the callback had succeeded. - Expose wrappers for some `DTLS diff --git a/src/OpenSSL/SSL.py b/src/OpenSSL/SSL.py index 6ef44d4..fa1b556 100644 --- a/src/OpenSSL/SSL.py +++ b/src/OpenSSL/SSL.py @@ -556,11 +556,18 @@ class _CookieGenerateCallbackHelper(_CallbackExceptionHelper): def __init__(self, callback): _CallbackExceptionHelper.__init__(self) + max_cookie_len = getattr(_lib, "DTLS1_COOKIE_LENGTH", 255) + @wraps(callback) def wrapper(ssl, out, outlen): 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 77e1876..fb77b75 100644 --- a/tests/test_ssl.py +++ b/tests/test_ssl.py @@ -4455,3 +4455,41 @@ class TestDTLS(object): assert 0 < c.get_cleartext_mtu() < 500 except NotImplementedError: # OpenSSL 1.1.0 and earlier pass + + 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() -- 2.25.1