mirror of
https://github.com/NixOS/nixpkgs.git
synced 2026-06-05 21:03:40 +00:00
ceph: pyopenssl CVE fixes
Belated fixes for some CVEs for the vendored pyopenssl. The Ceph source code directly is very unlikely to use (and in particular misuse) the affected parts of the API. Both `set_cookie_generate_callback` and `set_tlsext_servername_callback` have no actual occurrences in the tarball, so any use would be limited to dependencies, which would be hard to track. The major merge conflicts for backporting have been changes to the changlog which I've simply cut from the diff altogether. Contained should be the fixes and the tests only. Since this version of Ceph is phased out with the ongoing release of 26.05, moving to the new release and thus Ceph Tentacle is the recommended approach anyway, this is sort of a stopgap measure. Not-cherry-picked-because: only applicable to 25.11 Signed-off-by: benaryorg <binary@benary.org>
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
From 776f2a97d34c2ccfba90c0bcb448de7792edfdb6 Mon Sep 17 00:00:00 2001
|
||||
From: Alex Gaynor <alex.gaynor@gmail.com>
|
||||
Date: Wed, 18 Feb 2026 07:46:15 -0500
|
||||
Subject: [PATCH 1/2] 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 <noreply@anthropic.com>
|
||||
---
|
||||
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 efbf7907e618c912d48352f74fb80a9c19b9b98b..e28e10ab81ade8d79aff0cb9232fa71b1fb5314b 100644
|
||||
--- a/src/OpenSSL/SSL.py
|
||||
+++ b/src/OpenSSL/SSL.py
|
||||
@@ -561,11 +561,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 024436f064ddadbf79a3e6b78e2a9e4aeeee7ac2..5f427e92b48e57276fee7acb5ffdbaf136462cee 100644
|
||||
--- a/tests/test_ssl.py
|
||||
+++ b/tests/test_ssl.py
|
||||
@@ -4497,6 +4497,44 @@ class TestDTLS:
|
||||
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()
|
||||
+
|
||||
def test_timeout(self, monkeypatch):
|
||||
c_ctx = Context(DTLS_METHOD)
|
||||
c = Connection(c_ctx)
|
||||
--
|
||||
2.53.0
|
||||
|
||||
|
||||
From d39f020cc63c1da4d44be683f310fbc9f44f61bb Mon Sep 17 00:00:00 2001
|
||||
From: Alex Gaynor <alex.gaynor@gmail.com>
|
||||
Date: Mon, 16 Feb 2026 21:04:37 -0500
|
||||
Subject: [PATCH 2/2] 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 <noreply@anthropic.com>
|
||||
---
|
||||
src/OpenSSL/SSL.py | 9 +++++++--
|
||||
tests/test_ssl.py | 50 ++++++++++++++++++++++++++++++++++++++++++++++
|
||||
2 files changed, 57 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/src/OpenSSL/SSL.py b/src/OpenSSL/SSL.py
|
||||
index e28e10ab81ade8d79aff0cb9232fa71b1fb5314b..a2d5f5b086b3fe27c6e30848cdd027ee60f69677 100644
|
||||
--- a/src/OpenSSL/SSL.py
|
||||
+++ b/src/OpenSSL/SSL.py
|
||||
@@ -1,5 +1,6 @@
|
||||
import os
|
||||
import socket
|
||||
+import sys
|
||||
from errno import errorcode
|
||||
from functools import partial, wraps
|
||||
from itertools import chain, count
|
||||
@@ -1444,8 +1445,12 @@ class Context:
|
||||
"""
|
||||
|
||||
@wraps(callback)
|
||||
- def wrapper(ssl, alert, arg):
|
||||
- callback(Connection._reverse_mapping[ssl])
|
||||
+ def wrapper(ssl, alert, arg): # type: ignore[no-untyped-def]
|
||||
+ 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 5f427e92b48e57276fee7acb5ffdbaf136462cee..d42beace175c1ea79929050ec6f88faa539ff6b4 100644
|
||||
--- a/tests/test_ssl.py
|
||||
+++ b/tests/test_ssl.py
|
||||
@@ -1854,6 +1854,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.53.0
|
||||
|
||||
@@ -282,7 +282,9 @@ let
|
||||
inherit version;
|
||||
hash = "sha256-hBSYub7GFiOxtsR+u8AjZ8B9YODhlfGXkIF/EMyNsLc=";
|
||||
};
|
||||
patches = [ ]; # those two CVE patches do not apply (!)
|
||||
patches = [
|
||||
./old-python-packages/pyopenssl-Cherry-pick-fix-for-CVE-2026-27459-and-CVE-2026-27448.patch
|
||||
];
|
||||
disabledTests = old.disabledTests or [ ] ++ [
|
||||
"test_export_md5_digest"
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user