diff options
Diffstat (limited to 'meta/recipes-devtools/python')
8 files changed, 446 insertions, 1 deletions
diff --git a/meta/recipes-devtools/python/python3-cryptography/CVE-2026-26007.patch b/meta/recipes-devtools/python/python3-cryptography/CVE-2026-26007.patch new file mode 100644 index 0000000000..fb76bbfca3 --- /dev/null +++ b/meta/recipes-devtools/python/python3-cryptography/CVE-2026-26007.patch | |||
| @@ -0,0 +1,149 @@ | |||
| 1 | From 42c914929b52eb16421a4ef1f7e09c8f9fdab7db Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Paul Kehrer <paul.l.kehrer@gmail.com> | ||
| 3 | Date: Wed, 18 Mar 2026 16:01:03 +0900 | ||
| 4 | Subject: [PATCH] EC check key on cofactor > 1 | ||
| 5 | |||
| 6 | An attacker could create a malicious public key that reveals portions of | ||
| 7 | your private key when using certain uncommon elliptic curves (binary | ||
| 8 | curves). This version now includes additional security checks to | ||
| 9 | prevent this attack. This issue only affects binary elliptic curves, | ||
| 10 | which are rarely used in real-world applications. Credit to **XlabAI | ||
| 11 | Team of Tencent Xuanwu Lab and Atuin Automated Vulnerability Discovery | ||
| 12 | Engine** for reporting the issue. **CVE-2026-26007** | ||
| 13 | |||
| 14 | This is a partial backport of upstream commit | ||
| 15 | 0eebb9dbb6343d9bc1d91e5a2482ed4e054a6d8c, to only include what's | ||
| 16 | relevant for CVE-2026-26007. | ||
| 17 | |||
| 18 | CVE: CVE-2026-26007 | ||
| 19 | |||
| 20 | Origin: backport, https://github.com/pyca/cryptography/commit/0eebb9dbb6343d9bc1d91e5a2482ed4e054a6d8c | ||
| 21 | Reference: https://salsa.debian.org/python-team/packages/python-cryptography/-/commit/464e7ca3b0b4493d5906d0c3685de71fda770c59 | ||
| 22 | |||
| 23 | Signed-off-by: Nguyen Dat Tho <tho3.nguyen@lge.com> | ||
| 24 | Signed-off-by: Paul Kehrer <paul.l.kehrer@gmail.com> | ||
| 25 | Co-authored-by: Alex Gaynor <alex.gaynor@gmail.com> | ||
| 26 | |||
| 27 | Upstream-Status: Backport [Backport from https://github.com/pyca/cryptography/commit/0eebb9dbb6343d9bc1d91e5a2482ed4e054a6d8c] | ||
| 28 | --- | ||
| 29 | src/rust/src/backend/ec.rs | 39 ++++++++++++++++++++---------- | ||
| 30 | tests/hazmat/primitives/test_ec.py | 37 ++++++++++++++++++++++++++++ | ||
| 31 | 2 files changed, 63 insertions(+), 13 deletions(-) | ||
| 32 | |||
| 33 | diff --git a/src/rust/src/backend/ec.rs b/src/rust/src/backend/ec.rs | ||
| 34 | index 6a224b49f..27fced086 100644 | ||
| 35 | --- a/src/rust/src/backend/ec.rs | ||
| 36 | +++ b/src/rust/src/backend/ec.rs | ||
| 37 | @@ -155,12 +155,9 @@ pub(crate) fn public_key_from_pkey( | ||
| 38 | ) -> CryptographyResult<ECPublicKey> { | ||
| 39 | let ec = pkey.ec_key()?; | ||
| 40 | let curve = py_curve_from_curve(py, ec.group())?; | ||
| 41 | - check_key_infinity(&ec)?; | ||
| 42 | - Ok(ECPublicKey { | ||
| 43 | - pkey: pkey.to_owned(), | ||
| 44 | - curve: curve.into(), | ||
| 45 | - }) | ||
| 46 | + ECPublicKey::new(pkey.to_owned(), curve.into()) | ||
| 47 | } | ||
| 48 | + | ||
| 49 | #[pyo3::prelude::pyfunction] | ||
| 50 | fn generate_private_key( | ||
| 51 | py: pyo3::Python<'_>, | ||
| 52 | @@ -215,10 +212,7 @@ fn from_public_bytes( | ||
| 53 | let ec = openssl::ec::EcKey::from_public_key(&curve, &point)?; | ||
| 54 | let pkey = openssl::pkey::PKey::from_ec_key(ec)?; | ||
| 55 | |||
| 56 | - Ok(ECPublicKey { | ||
| 57 | - pkey, | ||
| 58 | - curve: py_curve.into(), | ||
| 59 | - }) | ||
| 60 | + ECPublicKey::new(pkey, py_curve.into()) | ||
| 61 | } | ||
| 62 | |||
| 63 | #[pyo3::prelude::pymethods] | ||
| 64 | @@ -357,6 +351,28 @@ impl ECPrivateKey { | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | +impl ECPublicKey { | ||
| 69 | + fn new( | ||
| 70 | + pkey: openssl::pkey::PKey<openssl::pkey::Public>, | ||
| 71 | + curve: pyo3::Py<pyo3::PyAny>, | ||
| 72 | + ) -> CryptographyResult<ECPublicKey> { | ||
| 73 | + let ec = pkey.ec_key()?; | ||
| 74 | + check_key_infinity(&ec)?; | ||
| 75 | + let mut bn_ctx = openssl::bn::BigNumContext::new()?; | ||
| 76 | + let mut cofactor = openssl::bn::BigNum::new()?; | ||
| 77 | + ec.group().cofactor(&mut cofactor, &mut bn_ctx)?; | ||
| 78 | + let one = openssl::bn::BigNum::from_u32(1)?; | ||
| 79 | + if cofactor != one { | ||
| 80 | + ec.check_key().map_err(|_| { | ||
| 81 | + pyo3::exceptions::PyValueError::new_err( | ||
| 82 | + "Invalid EC key (key out of range, infinity, etc.)", | ||
| 83 | + ) | ||
| 84 | + })?; | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + Ok(ECPublicKey { pkey, curve }) | ||
| 88 | + } | ||
| 89 | +} | ||
| 90 | #[pyo3::prelude::pymethods] | ||
| 91 | impl ECPublicKey { | ||
| 92 | #[getter] | ||
| 93 | @@ -591,10 +607,7 @@ impl EllipticCurvePublicNumbers { | ||
| 94 | |||
| 95 | let pkey = openssl::pkey::PKey::from_ec_key(public_key)?; | ||
| 96 | |||
| 97 | - Ok(ECPublicKey { | ||
| 98 | - pkey, | ||
| 99 | - curve: self.curve.clone_ref(py), | ||
| 100 | - }) | ||
| 101 | + ECPublicKey::new(pkey, self.curve.clone_ref(py)) | ||
| 102 | } | ||
| 103 | |||
| 104 | fn __eq__( | ||
| 105 | diff --git a/tests/hazmat/primitives/test_ec.py b/tests/hazmat/primitives/test_ec.py | ||
| 106 | index 334e76dcc..f7f2242f6 100644 | ||
| 107 | --- a/tests/hazmat/primitives/test_ec.py | ||
| 108 | +++ b/tests/hazmat/primitives/test_ec.py | ||
| 109 | @@ -1340,3 +1340,40 @@ class TestECDH: | ||
| 110 | |||
| 111 | with pytest.raises(ValueError): | ||
| 112 | key.exchange(ec.ECDH(), public_key) | ||
| 113 | + | ||
| 114 | + | ||
| 115 | +def test_invalid_sect_public_keys(backend): | ||
| 116 | + _skip_curve_unsupported(backend, ec.SECT571K1()) | ||
| 117 | + public_numbers = ec.EllipticCurvePublicNumbers(1, 1, ec.SECT571K1()) | ||
| 118 | + with pytest.raises(ValueError): | ||
| 119 | + public_numbers.public_key() | ||
| 120 | + | ||
| 121 | + point = binascii.unhexlify( | ||
| 122 | + b"0400000000000000000000000000000000000000000000000000000000000000000" | ||
| 123 | + b"0000000000000000000000000000000000000000000000000000000000000000000" | ||
| 124 | + b"0000000000010000000000000000000000000000000000000000000000000000000" | ||
| 125 | + b"0000000000000000000000000000000000000000000000000000000000000000000" | ||
| 126 | + b"0000000000000000000001" | ||
| 127 | + ) | ||
| 128 | + with pytest.raises(ValueError): | ||
| 129 | + ec.EllipticCurvePublicKey.from_encoded_point(ec.SECT571K1(), point) | ||
| 130 | + | ||
| 131 | + der = binascii.unhexlify( | ||
| 132 | + b"3081a7301006072a8648ce3d020106052b810400260381920004000000000000000" | ||
| 133 | + b"0000000000000000000000000000000000000000000000000000000000000000000" | ||
| 134 | + b"0000000000000000000000000000000000000000000000000000000000000100000" | ||
| 135 | + b"0000000000000000000000000000000000000000000000000000000000000000000" | ||
| 136 | + b"0000000000000000000000000000000000000000000000000000000000000000000" | ||
| 137 | + b"00001" | ||
| 138 | + ) | ||
| 139 | + with pytest.raises(ValueError): | ||
| 140 | + serialization.load_der_public_key(der) | ||
| 141 | + | ||
| 142 | + pem = textwrap.dedent("""-----BEGIN PUBLIC KEY----- | ||
| 143 | + MIGnMBAGByqGSM49AgEGBSuBBAAmA4GSAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||
| 144 | + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||
| 145 | + AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||
| 146 | + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE= | ||
| 147 | + -----END PUBLIC KEY-----""").encode() | ||
| 148 | + with pytest.raises(ValueError): | ||
| 149 | + serialization.load_pem_public_key(pem) | ||
diff --git a/meta/recipes-devtools/python/python3-cryptography_42.0.5.bb b/meta/recipes-devtools/python/python3-cryptography_42.0.5.bb index 732f925d92..c4573fa689 100644 --- a/meta/recipes-devtools/python/python3-cryptography_42.0.5.bb +++ b/meta/recipes-devtools/python/python3-cryptography_42.0.5.bb | |||
| @@ -11,6 +11,7 @@ LDSHARED += "-pthread" | |||
| 11 | SRC_URI[sha256sum] = "6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1" | 11 | SRC_URI[sha256sum] = "6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1" |
| 12 | 12 | ||
| 13 | SRC_URI += "file://0001-pyproject.toml-remove-benchmark-disable-option.patch \ | 13 | SRC_URI += "file://0001-pyproject.toml-remove-benchmark-disable-option.patch \ |
| 14 | file://CVE-2026-26007.patch \ | ||
| 14 | file://check-memfree.py \ | 15 | file://check-memfree.py \ |
| 15 | file://run-ptest \ | 16 | file://run-ptest \ |
| 16 | " | 17 | " |
diff --git a/meta/recipes-devtools/python/python3-pip/CVE-2026-1703.patch b/meta/recipes-devtools/python/python3-pip/CVE-2026-1703.patch new file mode 100644 index 0000000000..1470b7c541 --- /dev/null +++ b/meta/recipes-devtools/python/python3-pip/CVE-2026-1703.patch | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | From 4c651b70d60ed91b13663bcda9b3ed41748d0124 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Seth Michael Larson <seth@python.org> | ||
| 3 | Date: Fri, 30 Jan 2026 09:49:11 -0600 | ||
| 4 | Subject: [PATCH] Use os.path.commonpath() instead of commonprefix() | ||
| 5 | |||
| 6 | Upstream-Status: Backport [https://github.com/pypa/pip/commit/4c651b70d60ed91b13663bcda9b3ed41748d0124] | ||
| 7 | CVE: CVE-2026-1703 | ||
| 8 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
| 9 | --- | ||
| 10 | news/+1ee322a1.bugfix.rst | 1 + | ||
| 11 | src/pip/_internal/utils/unpacking.py | 2 +- | ||
| 12 | 2 files changed, 2 insertions(+), 1 deletion(-) | ||
| 13 | create mode 100644 news/+1ee322a1.bugfix.rst | ||
| 14 | |||
| 15 | diff --git a/news/+1ee322a1.bugfix.rst b/news/+1ee322a1.bugfix.rst | ||
| 16 | new file mode 100644 | ||
| 17 | index 0000000..edb1b32 | ||
| 18 | --- /dev/null | ||
| 19 | +++ b/news/+1ee322a1.bugfix.rst | ||
| 20 | @@ -0,0 +1 @@ | ||
| 21 | +Use a path-segment prefix comparison, not char-by-char. | ||
| 22 | diff --git a/src/pip/_internal/utils/unpacking.py b/src/pip/_internal/utils/unpacking.py | ||
| 23 | index 78b5c13..0b26525 100644 | ||
| 24 | --- a/src/pip/_internal/utils/unpacking.py | ||
| 25 | +++ b/src/pip/_internal/utils/unpacking.py | ||
| 26 | @@ -81,7 +81,7 @@ def is_within_directory(directory: str, target: str) -> bool: | ||
| 27 | abs_directory = os.path.abspath(directory) | ||
| 28 | abs_target = os.path.abspath(target) | ||
| 29 | |||
| 30 | - prefix = os.path.commonprefix([abs_directory, abs_target]) | ||
| 31 | + prefix = os.path.commonpath([abs_directory, abs_target]) | ||
| 32 | return prefix == abs_directory | ||
| 33 | |||
| 34 | |||
| 35 | -- | ||
| 36 | 2.43.0 | ||
| 37 | |||
diff --git a/meta/recipes-devtools/python/python3-pip_24.0.bb b/meta/recipes-devtools/python/python3-pip_24.0.bb index be4a29500a..cf123a5d23 100644 --- a/meta/recipes-devtools/python/python3-pip_24.0.bb +++ b/meta/recipes-devtools/python/python3-pip_24.0.bb | |||
| @@ -31,7 +31,9 @@ LIC_FILES_CHKSUM = "file://LICENSE.txt;md5=63ec52baf95163b597008bb46db68030 \ | |||
| 31 | 31 | ||
| 32 | inherit pypi python_setuptools_build_meta | 32 | inherit pypi python_setuptools_build_meta |
| 33 | 33 | ||
| 34 | SRC_URI += "file://no_shebang_mangling.patch" | 34 | SRC_URI += "file://no_shebang_mangling.patch \ |
| 35 | file://CVE-2026-1703.patch \ | ||
| 36 | " | ||
| 35 | 37 | ||
| 36 | SRC_URI[sha256sum] = "ea9bd1a847e8c5774a5777bb398c19e80bcd4e2aa16a4b301b718fe6f593aba2" | 38 | SRC_URI[sha256sum] = "ea9bd1a847e8c5774a5777bb398c19e80bcd4e2aa16a4b301b718fe6f593aba2" |
| 37 | 39 | ||
| @@ -39,6 +41,15 @@ do_install:append() { | |||
| 39 | rm -f ${D}/${bindir}/pip | 41 | rm -f ${D}/${bindir}/pip |
| 40 | } | 42 | } |
| 41 | 43 | ||
| 44 | do_install:append(){ | ||
| 45 | # pip vendors distlib which ships Windows launcher templates (*.exe). | ||
| 46 | # Keep them only when building for a Windows (mingw) host. | ||
| 47 | case "${HOST_OS}" in | ||
| 48 | mingw32|mingw64) ;; | ||
| 49 | *) rm -f ${D}${PYTHON_SITEPACKAGES_DIR}/pip/_vendor/distlib/*.exe ;; | ||
| 50 | esac | ||
| 51 | } | ||
| 52 | |||
| 42 | RDEPENDS:${PN} = "\ | 53 | RDEPENDS:${PN} = "\ |
| 43 | python3-compile \ | 54 | python3-compile \ |
| 44 | python3-io \ | 55 | python3-io \ |
diff --git a/meta/recipes-devtools/python/python3-pyopenssl/CVE-2026-27448.patch b/meta/recipes-devtools/python/python3-pyopenssl/CVE-2026-27448.patch new file mode 100644 index 0000000000..87f46b4cb0 --- /dev/null +++ b/meta/recipes-devtools/python/python3-pyopenssl/CVE-2026-27448.patch | |||
| @@ -0,0 +1,124 @@ | |||
| 1 | From d41a814759a9fb49584ca8ab3f7295de49a85aa0 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Alex Gaynor <alex.gaynor@gmail.com> | ||
| 3 | Date: Mon, 16 Feb 2026 21:04:37 -0500 | ||
| 4 | Subject: [PATCH] Handle exceptions in set_tlsext_servername_callback callbacks | ||
| 5 | (#1478) | ||
| 6 | |||
| 7 | When the servername callback raises an exception, call sys.excepthook | ||
| 8 | with the exception info and return SSL_TLSEXT_ERR_ALERT_FATAL to abort | ||
| 9 | the handshake. Previously, exceptions would propagate uncaught through | ||
| 10 | the CFFI callback boundary. | ||
| 11 | |||
| 12 | https://claude.ai/code/session_01P7y1XmWkdtC5UcmZwGDvGi | ||
| 13 | |||
| 14 | Co-authored-by: Claude <noreply@anthropic.com> | ||
| 15 | |||
| 16 | Upstream-Status: Backport [https://github.com/pyca/pyopenssl/commit/d41a814759a9fb49584ca8ab3f7295de49a85aa0] | ||
| 17 | CVE: CVE-2026-27448 | ||
| 18 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
| 19 | --- | ||
| 20 | CHANGELOG.rst | 1 + | ||
| 21 | src/OpenSSL/SSL.py | 7 ++++++- | ||
| 22 | tests/test_ssl.py | 50 ++++++++++++++++++++++++++++++++++++++++++++++ | ||
| 23 | 3 files changed, 57 insertions(+), 1 deletion(-) | ||
| 24 | |||
| 25 | diff --git a/CHANGELOG.rst b/CHANGELOG.rst | ||
| 26 | index 6e23770..12e60e4 100644 | ||
| 27 | --- a/CHANGELOG.rst | ||
| 28 | +++ b/CHANGELOG.rst | ||
| 29 | @@ -18,6 +18,7 @@ Changes: | ||
| 30 | |||
| 31 | - Added ``OpenSSL.SSL.Connection.get_selected_srtp_profile`` to determine which SRTP profile was negotiated. | ||
| 32 | `#1279 <https://github.com/pyca/pyopenssl/pull/1279>`_. | ||
| 33 | +- ``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. | ||
| 34 | |||
| 35 | 23.3.0 (2023-10-25) | ||
| 36 | ------------------- | ||
| 37 | diff --git a/src/OpenSSL/SSL.py b/src/OpenSSL/SSL.py | ||
| 38 | index 4db5240..a6263c4 100644 | ||
| 39 | --- a/src/OpenSSL/SSL.py | ||
| 40 | +++ b/src/OpenSSL/SSL.py | ||
| 41 | @@ -1,5 +1,6 @@ | ||
| 42 | import os | ||
| 43 | import socket | ||
| 44 | +import sys | ||
| 45 | import typing | ||
| 46 | from errno import errorcode | ||
| 47 | from functools import partial, wraps | ||
| 48 | @@ -1567,7 +1568,11 @@ class Context: | ||
| 49 | |||
| 50 | @wraps(callback) | ||
| 51 | def wrapper(ssl, alert, arg): | ||
| 52 | - callback(Connection._reverse_mapping[ssl]) | ||
| 53 | + try: | ||
| 54 | + callback(Connection._reverse_mapping[ssl]) | ||
| 55 | + except Exception: | ||
| 56 | + sys.excepthook(*sys.exc_info()) | ||
| 57 | + return _lib.SSL_TLSEXT_ERR_ALERT_FATAL | ||
| 58 | return 0 | ||
| 59 | |||
| 60 | self._tlsext_servername_callback = _ffi.callback( | ||
| 61 | diff --git a/tests/test_ssl.py b/tests/test_ssl.py | ||
| 62 | index ca5bf83..55489b9 100644 | ||
| 63 | --- a/tests/test_ssl.py | ||
| 64 | +++ b/tests/test_ssl.py | ||
| 65 | @@ -1855,6 +1855,56 @@ class TestServerNameCallback: | ||
| 66 | |||
| 67 | assert args == [(server, b"foo1.example.com")] | ||
| 68 | |||
| 69 | + def test_servername_callback_exception( | ||
| 70 | + self, monkeypatch: pytest.MonkeyPatch | ||
| 71 | + ) -> None: | ||
| 72 | + """ | ||
| 73 | + When the callback passed to `Context.set_tlsext_servername_callback` | ||
| 74 | + raises an exception, ``sys.excepthook`` is called with the exception | ||
| 75 | + and the handshake fails with an ``Error``. | ||
| 76 | + """ | ||
| 77 | + exc = TypeError("server name callback failed") | ||
| 78 | + | ||
| 79 | + def servername(conn: Connection) -> None: | ||
| 80 | + raise exc | ||
| 81 | + | ||
| 82 | + excepthook_calls: list[ | ||
| 83 | + tuple[type[BaseException], BaseException, object] | ||
| 84 | + ] = [] | ||
| 85 | + | ||
| 86 | + def custom_excepthook( | ||
| 87 | + exc_type: type[BaseException], | ||
| 88 | + exc_value: BaseException, | ||
| 89 | + exc_tb: object, | ||
| 90 | + ) -> None: | ||
| 91 | + excepthook_calls.append((exc_type, exc_value, exc_tb)) | ||
| 92 | + | ||
| 93 | + context = Context(SSLv23_METHOD) | ||
| 94 | + context.set_tlsext_servername_callback(servername) | ||
| 95 | + | ||
| 96 | + # Necessary to actually accept the connection | ||
| 97 | + context.use_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem)) | ||
| 98 | + context.use_certificate( | ||
| 99 | + load_certificate(FILETYPE_PEM, server_cert_pem) | ||
| 100 | + ) | ||
| 101 | + | ||
| 102 | + # Do a little connection to trigger the logic | ||
| 103 | + server = Connection(context, None) | ||
| 104 | + server.set_accept_state() | ||
| 105 | + | ||
| 106 | + client = Connection(Context(SSLv23_METHOD), None) | ||
| 107 | + client.set_connect_state() | ||
| 108 | + client.set_tlsext_host_name(b"foo1.example.com") | ||
| 109 | + | ||
| 110 | + monkeypatch.setattr(sys, "excepthook", custom_excepthook) | ||
| 111 | + with pytest.raises(Error): | ||
| 112 | + interact_in_memory(server, client) | ||
| 113 | + | ||
| 114 | + assert len(excepthook_calls) == 1 | ||
| 115 | + assert excepthook_calls[0][0] is TypeError | ||
| 116 | + assert excepthook_calls[0][1] is exc | ||
| 117 | + assert excepthook_calls[0][2] is not None | ||
| 118 | + | ||
| 119 | |||
| 120 | class TestApplicationLayerProtoNegotiation: | ||
| 121 | """ | ||
| 122 | -- | ||
| 123 | 2.43.0 | ||
| 124 | |||
diff --git a/meta/recipes-devtools/python/python3-pyopenssl/CVE-2026-27459.patch b/meta/recipes-devtools/python/python3-pyopenssl/CVE-2026-27459.patch new file mode 100644 index 0000000000..f75540f96e --- /dev/null +++ b/meta/recipes-devtools/python/python3-pyopenssl/CVE-2026-27459.patch | |||
| @@ -0,0 +1,109 @@ | |||
| 1 | From 57f09bb4bb051d3bc2a1abd36e9525313d5cd408 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Alex Gaynor <alex.gaynor@gmail.com> | ||
| 3 | Date: Wed, 18 Feb 2026 07:46:15 -0500 | ||
| 4 | Subject: [PATCH] Fix buffer overflow in DTLS cookie generation callback | ||
| 5 | (#1479) | ||
| 6 | |||
| 7 | The cookie generate callback copied user-returned bytes into a | ||
| 8 | fixed-size native buffer without enforcing a maximum length. A | ||
| 9 | callback returning more than DTLS1_COOKIE_LENGTH bytes would overflow | ||
| 10 | the OpenSSL-provided buffer, corrupting adjacent memory. | ||
| 11 | |||
| 12 | Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> | ||
| 13 | |||
| 14 | Upstream-Status: Backport [https://github.com/pyca/pyopenssl/commit/57f09bb4bb051d3bc2a1abd36e9525313d5cd408] | ||
| 15 | CVE: CVE-2026-27459 | ||
| 16 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
| 17 | --- | ||
| 18 | CHANGELOG.rst | 1 + | ||
| 19 | src/OpenSSL/SSL.py | 7 +++++++ | ||
| 20 | tests/test_ssl.py | 38 ++++++++++++++++++++++++++++++++++++++ | ||
| 21 | 3 files changed, 46 insertions(+) | ||
| 22 | |||
| 23 | diff --git a/CHANGELOG.rst b/CHANGELOG.rst | ||
| 24 | index 12e60e4..6041fdc 100644 | ||
| 25 | --- a/CHANGELOG.rst | ||
| 26 | +++ b/CHANGELOG.rst | ||
| 27 | @@ -16,6 +16,7 @@ Deprecations: | ||
| 28 | Changes: | ||
| 29 | ^^^^^^^^ | ||
| 30 | |||
| 31 | +- 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. | ||
| 32 | - Added ``OpenSSL.SSL.Connection.get_selected_srtp_profile`` to determine which SRTP profile was negotiated. | ||
| 33 | `#1279 <https://github.com/pyca/pyopenssl/pull/1279>`_. | ||
| 34 | - ``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. | ||
| 35 | diff --git a/src/OpenSSL/SSL.py b/src/OpenSSL/SSL.py | ||
| 36 | index a6263c4..2e4da78 100644 | ||
| 37 | --- a/src/OpenSSL/SSL.py | ||
| 38 | +++ b/src/OpenSSL/SSL.py | ||
| 39 | @@ -691,11 +691,18 @@ class _CookieGenerateCallbackHelper(_CallbackExceptionHelper): | ||
| 40 | def __init__(self, callback): | ||
| 41 | _CallbackExceptionHelper.__init__(self) | ||
| 42 | |||
| 43 | + max_cookie_len = getattr(_lib, "DTLS1_COOKIE_LENGTH", 255) | ||
| 44 | + | ||
| 45 | @wraps(callback) | ||
| 46 | def wrapper(ssl, out, outlen): | ||
| 47 | try: | ||
| 48 | conn = Connection._reverse_mapping[ssl] | ||
| 49 | cookie = callback(conn) | ||
| 50 | + if len(cookie) > max_cookie_len: | ||
| 51 | + raise ValueError( | ||
| 52 | + f"Cookie too long (got {len(cookie)} bytes, " | ||
| 53 | + f"max {max_cookie_len})" | ||
| 54 | + ) | ||
| 55 | out[0 : len(cookie)] = cookie | ||
| 56 | outlen[0] = len(cookie) | ||
| 57 | return 1 | ||
| 58 | diff --git a/tests/test_ssl.py b/tests/test_ssl.py | ||
| 59 | index 55489b9..683e368 100644 | ||
| 60 | --- a/tests/test_ssl.py | ||
| 61 | +++ b/tests/test_ssl.py | ||
| 62 | @@ -4560,6 +4560,44 @@ class TestDTLS: | ||
| 63 | def test_it_works_with_srtp(self): | ||
| 64 | self._test_handshake_and_data(srtp_profile=b"SRTP_AES128_CM_SHA1_80") | ||
| 65 | |||
| 66 | + def test_cookie_generate_too_long(self) -> None: | ||
| 67 | + s_ctx = Context(DTLS_METHOD) | ||
| 68 | + | ||
| 69 | + def generate_cookie(ssl: Connection) -> bytes: | ||
| 70 | + return b"\x00" * 256 | ||
| 71 | + | ||
| 72 | + def verify_cookie(ssl: Connection, cookie: bytes) -> bool: | ||
| 73 | + return True | ||
| 74 | + | ||
| 75 | + s_ctx.set_cookie_generate_callback(generate_cookie) | ||
| 76 | + s_ctx.set_cookie_verify_callback(verify_cookie) | ||
| 77 | + s_ctx.use_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem)) | ||
| 78 | + s_ctx.use_certificate(load_certificate(FILETYPE_PEM, server_cert_pem)) | ||
| 79 | + s_ctx.set_options(OP_NO_QUERY_MTU) | ||
| 80 | + s = Connection(s_ctx) | ||
| 81 | + s.set_accept_state() | ||
| 82 | + | ||
| 83 | + c_ctx = Context(DTLS_METHOD) | ||
| 84 | + c_ctx.set_options(OP_NO_QUERY_MTU) | ||
| 85 | + c = Connection(c_ctx) | ||
| 86 | + c.set_connect_state() | ||
| 87 | + | ||
| 88 | + c.set_ciphertext_mtu(1500) | ||
| 89 | + s.set_ciphertext_mtu(1500) | ||
| 90 | + | ||
| 91 | + # Client sends ClientHello | ||
| 92 | + try: | ||
| 93 | + c.do_handshake() | ||
| 94 | + except SSL.WantReadError: | ||
| 95 | + pass | ||
| 96 | + chunk = c.bio_read(self.LARGE_BUFFER) | ||
| 97 | + s.bio_write(chunk) | ||
| 98 | + | ||
| 99 | + # Server tries DTLSv1_listen, which triggers cookie generation. | ||
| 100 | + # The oversized cookie should raise ValueError. | ||
| 101 | + with pytest.raises(ValueError, match="Cookie too long"): | ||
| 102 | + s.DTLSv1_listen() | ||
| 103 | + | ||
| 104 | def test_timeout(self, monkeypatch): | ||
| 105 | c_ctx = Context(DTLS_METHOD) | ||
| 106 | c = Connection(c_ctx) | ||
| 107 | -- | ||
| 108 | 2.43.0 | ||
| 109 | |||
diff --git a/meta/recipes-devtools/python/python3-pyopenssl_24.0.0.bb b/meta/recipes-devtools/python/python3-pyopenssl_24.0.0.bb index 116f214bfa..94a70aa17d 100644 --- a/meta/recipes-devtools/python/python3-pyopenssl_24.0.0.bb +++ b/meta/recipes-devtools/python/python3-pyopenssl_24.0.0.bb | |||
| @@ -10,6 +10,11 @@ SRC_URI[sha256sum] = "6aa33039a93fffa4563e655b61d11364d01264be8ccb49906101e02a33 | |||
| 10 | PYPI_PACKAGE = "pyOpenSSL" | 10 | PYPI_PACKAGE = "pyOpenSSL" |
| 11 | inherit pypi setuptools3 | 11 | inherit pypi setuptools3 |
| 12 | 12 | ||
| 13 | SRC_URI += " \ | ||
| 14 | file://CVE-2026-27448.patch \ | ||
| 15 | file://CVE-2026-27459.patch \ | ||
| 16 | " | ||
| 17 | |||
| 13 | PACKAGES =+ "${PN}-tests" | 18 | PACKAGES =+ "${PN}-tests" |
| 14 | FILES:${PN}-tests = "${libdir}/${PYTHON_DIR}/site-packages/OpenSSL/test" | 19 | FILES:${PN}-tests = "${libdir}/${PYTHON_DIR}/site-packages/OpenSSL/test" |
| 15 | 20 | ||
diff --git a/meta/recipes-devtools/python/python3-setuptools_69.1.1.bb b/meta/recipes-devtools/python/python3-setuptools_69.1.1.bb index 46b2f0ab00..00f83056db 100644 --- a/meta/recipes-devtools/python/python3-setuptools_69.1.1.bb +++ b/meta/recipes-devtools/python/python3-setuptools_69.1.1.bb | |||
| @@ -19,6 +19,15 @@ SRC_URI += " \ | |||
| 19 | 19 | ||
| 20 | SRC_URI[sha256sum] = "5c0806c7d9af348e6dd3777b4f4dbb42c7ad85b190104837488eab9a7c945cf8" | 20 | SRC_URI[sha256sum] = "5c0806c7d9af348e6dd3777b4f4dbb42c7ad85b190104837488eab9a7c945cf8" |
| 21 | 21 | ||
| 22 | do_install:append() { | ||
| 23 | # setuptools ships Windows launcher executables (cli*.exe, gui*.exe). | ||
| 24 | # Keep them only when building for a Windows (mingw) host. | ||
| 25 | case "${HOST_OS}" in | ||
| 26 | mingw32|mingw64) ;; | ||
| 27 | *) rm -f ${D}${PYTHON_SITEPACKAGES_DIR}/setuptools/*.exe ;; | ||
| 28 | esac | ||
| 29 | } | ||
| 30 | |||
| 22 | DEPENDS += "python3" | 31 | DEPENDS += "python3" |
| 23 | 32 | ||
| 24 | RDEPENDS:${PN} = "\ | 33 | RDEPENDS:${PN} = "\ |
