From e6e0f3539b88e2d849f791415b0a02755afbb03c Mon Sep 17 00:00:00 2001 From: Julien Rische Date: Wed, 19 Nov 2025 16:27:27 +0100 Subject: [PATCH] python-kdcproxy-1.1.0-1 - New upstream version (1.1.0) - Use DNS discovery for declared realms only (CVE-2025-59088) Resolves: RHEL-113677 - Fix DoS vulnerability based on unbounded TCP buffering (CVE-2025-59089) Resolves: RHEL-113681 Signed-off-by: Julien Rische --- .gitignore | 2 + Drop-coverage-from-tests.patch | 100 ----------- Use-dedicated-kdcproxy-logger.patch | 71 -------- ...ntial-backoff-for-connection-retries.patch | 158 ------------------ python-kdcproxy.spec | 51 +++--- sources | 3 +- 6 files changed, 32 insertions(+), 353 deletions(-) delete mode 100644 Drop-coverage-from-tests.patch delete mode 100644 Use-dedicated-kdcproxy-logger.patch delete mode 100644 Use-exponential-backoff-for-connection-retries.patch diff --git a/.gitignore b/.gitignore index 1114f83..9d6a97f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ /kdcproxy-0.4.1.tar.gz /kdcproxy-0.4.2.tar.gz /kdcproxy-1.0.0.tar.gz +/kdcproxy-1.1.0.tar.gz +/kdcproxy-1.1.0.tar.gz.sha512sum.txt diff --git a/Drop-coverage-from-tests.patch b/Drop-coverage-from-tests.patch deleted file mode 100644 index f3c5c29..0000000 --- a/Drop-coverage-from-tests.patch +++ /dev/null @@ -1,100 +0,0 @@ -From 7b7aee01d72be5a310678cdad189cb7382f28549 Mon Sep 17 00:00:00 2001 -From: Robbie Harwood -Date: Tue, 19 Jan 2021 11:41:40 -0500 -Subject: [PATCH] Drop coverage from tests - -To my knowledge, we've never looked at or done anything with this -output. Test coverage is a noble goal, but this project is mostly -complete, so we don't expect heavy development soon. - -Requested-by: Petr Viktorin -Signed-off-by: Robbie Harwood -(cherry picked from commit 86c3da13d5d6cdb5822d194f2b820da1fd31dddb) -[rharwood@redhat.com: .gitignore] ---- - .coveragerc | 23 ----------------------- - MANIFEST.in | 1 - - setup.py | 2 +- - tox.ini | 12 ++---------- - 4 files changed, 3 insertions(+), 35 deletions(-) - delete mode 100644 .coveragerc - -diff --git a/.coveragerc b/.coveragerc -deleted file mode 100644 -index 4038562..0000000 ---- a/.coveragerc -+++ /dev/null -@@ -1,23 +0,0 @@ --[run] --branch = True --source = -- kdcproxy -- tests.py -- --[paths] --source = -- kdcproxy -- .tox/*/lib/python*/site-packages/kdcproxy -- --[report] --ignore_errors = False --precision = 1 --exclude_lines = -- pragma: no cover -- raise AssertionError -- raise NotImplementedError -- if 0: -- if False: -- if __name__ == .__main__.: -- if PY3 -- if not PY3 -diff --git a/MANIFEST.in b/MANIFEST.in -index 362f840..ff6b9a7 100644 ---- a/MANIFEST.in -+++ b/MANIFEST.in -@@ -2,4 +2,3 @@ include README COPYING - include tox.ini - include setup.cfg - include tests.py tests.krb5.conf --include .coveragerc -diff --git a/setup.py b/setup.py -index 20b335e..4b34fcc 100644 ---- a/setup.py -+++ b/setup.py -@@ -29,7 +29,7 @@ install_requires = [ - ] - - extras_require = { -- "tests": ["pytest", "coverage", "WebTest"], -+ "tests": ["pytest", "WebTest"], - "test_pep8": ['flake8', 'flake8-import-order', 'pep8-naming'] - } - -diff --git a/tox.ini b/tox.ini -index 038d996..9672cee 100644 ---- a/tox.ini -+++ b/tox.ini -@@ -1,21 +1,13 @@ - [tox] - minversion = 2.3.1 --envlist = py36,py37,py38,py39,pep8,py3pep8,doc,coverage-report -+envlist = py36,py37,py38,py39,pep8,py3pep8,doc - skip_missing_interpreters = true - - [testenv] - deps = - .[tests] - commands = -- {envpython} -m coverage run --parallel \ -- -m pytest --capture=no --strict {posargs} -- --[testenv:coverage-report] --deps = coverage --skip_install = true --commands = -- {envpython} -m coverage combine -- {envpython} -m coverage report --show-missing -+ {envpython} -m pytest --capture=no --strict {posargs} - - [testenv:pep8] - basepython = python3 diff --git a/Use-dedicated-kdcproxy-logger.patch b/Use-dedicated-kdcproxy-logger.patch deleted file mode 100644 index dda863a..0000000 --- a/Use-dedicated-kdcproxy-logger.patch +++ /dev/null @@ -1,71 +0,0 @@ -From 99babf4ba3ce4d1f5bb893e7678df44d16b74d03 Mon Sep 17 00:00:00 2001 -From: Julien Rische -Date: Mon, 18 Nov 2024 10:01:16 +0100 -Subject: [PATCH] Use dedicated "kdcproxy" logger - -Signed-off-by: Julien Rische -(cherry picked from commit c8a69dbc0777579ba3bf3d156baed0966327ebc2) ---- - kdcproxy/__init__.py | 7 +++++-- - kdcproxy/config/__init__.py | 7 +++++-- - 2 files changed, 10 insertions(+), 4 deletions(-) - -diff --git a/kdcproxy/__init__.py b/kdcproxy/__init__.py -index d0ca43e..ce96a0c 100644 ---- a/kdcproxy/__init__.py -+++ b/kdcproxy/__init__.py -@@ -38,6 +38,9 @@ else: - import httplib - import urlparse - -+logging.basicConfig() -+logger = logging.getLogger('kdcproxy') -+ - - class HTTPException(Exception): - -@@ -327,8 +330,8 @@ class Application: - fail_socktype = self.addr2socktypename(fail_addr) - fail_ip = fail_addr[4][0] - fail_port = fail_addr[4][1] -- logging.warning("Exchange with %s:[%s]:%d failed: %s", -- fail_socktype, fail_ip, fail_port, e) -+ logger.warning("Exchange with %s:[%s]:%d failed: %s", -+ fail_socktype, fail_ip, fail_port, e) - if reply is not None: - break - -diff --git a/kdcproxy/config/__init__.py b/kdcproxy/config/__init__.py -index a1435b7..8e17c5b 100644 ---- a/kdcproxy/config/__init__.py -+++ b/kdcproxy/config/__init__.py -@@ -32,6 +32,9 @@ except ImportError: # Python 2.x - import dns.rdatatype - import dns.resolver - -+logging.basicConfig() -+logger = logging.getLogger('kdcproxy') -+ - - class IResolver(object): - -@@ -60,14 +63,14 @@ class KDCProxyConfig(IConfig): - try: - self.__cp.read(filenames) - except configparser.Error: -- logging.error("Unable to read config file(s): %s", filenames) -+ logger.error("Unable to read config file(s): %s", filenames) - - try: - mod = self.__cp.get(self.GLOBAL, "configs") - try: - importlib.import_module("kdcproxy.config." + mod) - except ImportError as e: -- logging.log(logging.ERROR, "Error reading config: %s" % e) -+ logger.log(logging.ERROR, "Error reading config: %s" % e) - except configparser.Error: - pass - --- -2.46.0 - diff --git a/Use-exponential-backoff-for-connection-retries.patch b/Use-exponential-backoff-for-connection-retries.patch deleted file mode 100644 index 3939c41..0000000 --- a/Use-exponential-backoff-for-connection-retries.patch +++ /dev/null @@ -1,158 +0,0 @@ -From 0b2efa7b2901ada01758a0525a21af5447aa647a Mon Sep 17 00:00:00 2001 -From: Julien Rische -Date: Mon, 18 Nov 2024 09:38:13 +0100 -Subject: [PATCH] Use exponential backoff for connection retries - -Calls to socket.connect() are non-blocking, hence all subsequent calls -to socket.sendall() will fail if the target KDC service is temporarily -or indefinitely unreachable. Since the kdcproxy task uses busy-looping, -it results in the journal to be flooded with warning logs. - -This commit introduces a per-socket reactivation delay which increases -exponentially as the number of reties is incremented, until timeout is -reached (i.e. 100ms, 200ms, 400ms, 800ms, 1.6s, 3.2s, ...). - -Signed-off-by: Julien Rische -(cherry picked from commit bac3c99c1b23487e38d965a79173ce9519e19c75) ---- - kdcproxy/__init__.py | 63 +++++++++++++++++++++++++++++++++++++++++--- - 1 file changed, 60 insertions(+), 3 deletions(-) - -diff --git a/kdcproxy/__init__.py b/kdcproxy/__init__.py -index 1493b30..d0ca43e 100644 ---- a/kdcproxy/__init__.py -+++ b/kdcproxy/__init__.py -@@ -61,6 +61,13 @@ class HTTPException(Exception): - return "%d %s" % (self.code, httplib.responses[self.code]) - - -+class SocketException(Exception): -+ -+ def __init__(self, message, sock): -+ super(Exception, self).__init__(message) -+ self.sockfno = sock.fileno() -+ -+ - class Application: - MAX_LENGTH = 128 * 1024 - SOCKTYPES = { -@@ -68,10 +75,23 @@ class Application: - "udp": socket.SOCK_DGRAM, - } - -+ def addr2socktypename(self, addr): -+ ret = None -+ for name in self.SOCKTYPES: -+ if self.SOCKTYPES[name] == addr[1]: -+ ret = name -+ break -+ return ret -+ - def __init__(self): - self.__resolver = MetaResolver() - - def __await_reply(self, pr, rsocks, wsocks, timeout): -+ starting_time = time.time() -+ send_error = None -+ recv_error = None -+ failing_sock = None -+ reactivations = {} - extra = 0 - read_buffers = {} - while (timeout + extra) > time.time(): -@@ -92,6 +112,12 @@ class Application: - pass - - for sock in w: -+ # Fetch reactivation tuple: -+ # 1st element: reactivation index (-1 = first activation) -+ # 2nd element: planned reactivation time (0.0 = now) -+ (rn, rt) = reactivations.get(sock, (-1, 0.0)) -+ if rt > time.time(): -+ continue - try: - if self.sock_type(sock) == socket.SOCK_DGRAM: - # If we proxy over UDP, remove the 4-byte length -@@ -101,8 +127,13 @@ class Application: - sock.sendall(pr.request) - extra = 10 # New connections get 10 extra seconds - except Exception as e: -- logging.warning("Conection broken while writing (%s)", e) -+ send_error = e -+ failing_sock = sock -+ reactivations[sock] = (rn + 1, -+ time.time() + 2.0**(rn + 1) / 10) - continue -+ if sock in reactivations: -+ del reactivations[sock] - rsocks.append(sock) - wsocks.remove(sock) - -@@ -110,7 +141,8 @@ class Application: - try: - reply = self.__handle_recv(sock, read_buffers) - except Exception as e: -- logging.warning("Connection broken while reading (%s)", e) -+ recv_error = e -+ failing_sock = sock - if self.sock_type(sock) == socket.SOCK_STREAM: - # Remove broken TCP socket from readers - rsocks.remove(sock) -@@ -118,6 +150,21 @@ class Application: - if reply is not None: - return reply - -+ if reactivations: -+ raise SocketException("Timeout while sending packets after %.2fs " -+ "and %d tries: %s" % ( -+ (timeout + extra) - starting_time, -+ sum(map(lambda r: r[0], -+ reactivations.values())), -+ send_error), -+ failing_sock) -+ elif recv_error is not None: -+ raise SocketException("Timeout while receiving packets after " -+ "%.2fs: %s" % ( -+ (timeout + extra) - starting_time, -+ recv_error), -+ failing_sock) -+ - return None - - def __handle_recv(self, sock, read_buffers): -@@ -215,6 +262,7 @@ class Application: - reply = None - wsocks = [] - rsocks = [] -+ sockfno2addr = {} - for server in map(urlparse.urlparse, servers): - # Enforce valid, supported URIs - scheme = server.scheme.lower().split("+", 1) -@@ -261,6 +309,7 @@ class Application: - continue - except io.BlockingIOError: - pass -+ sockfno2addr[sock.fileno()] = addr - wsocks.append(sock) - - # Resend packets to UDP servers -@@ -271,7 +320,15 @@ class Application: - - # Call select() - timeout = time.time() + (15 if addr is None else 2) -- reply = self.__await_reply(pr, rsocks, wsocks, timeout) -+ try: -+ reply = self.__await_reply(pr, rsocks, wsocks, timeout) -+ except SocketException as e: -+ fail_addr = sockfno2addr[e.sockfno] -+ fail_socktype = self.addr2socktypename(fail_addr) -+ fail_ip = fail_addr[4][0] -+ fail_port = fail_addr[4][1] -+ logging.warning("Exchange with %s:[%s]:%d failed: %s", -+ fail_socktype, fail_ip, fail_port, e) - if reply is not None: - break - --- -2.46.0 - diff --git a/python-kdcproxy.spec b/python-kdcproxy.spec index 4fe846d..6d5b59b 100644 --- a/python-kdcproxy.spec +++ b/python-kdcproxy.spec @@ -1,31 +1,32 @@ %global realname kdcproxy Name: python-%{realname} -Version: 1.0.0 -Release: 8%{?dist} +Version: 1.1.0 +Release: 1%{?dist} Summary: MS-KKDCP (kerberos proxy) WSGI module License: MIT URL: https://github.com/latchset/%{realname} -Source0: https://github.com/latchset/%{realname}/archive/%{realname}-%{version}.tar.gz +Source0: https://github.com/latchset/%{realname}/releases/download/v%{version}/%{realname}-%{version}.tar.gz +Source1: https://github.com/latchset/%{realname}/releases/download/v%{version}/%{realname}-%{version}.tar.gz.sha512sum.txt -Patch0: Drop-coverage-from-tests.patch -Patch1: Use-exponential-backoff-for-connection-retries.patch -Patch2: Use-dedicated-kdcproxy-logger.patch +# Patches BuildArch: noarch -BuildRequires: git -BuildRequires: python3-devel -BuildRequires: python3-dns -BuildRequires: python3-pyasn1 +BuildRequires: git-core BuildRequires: python3-pytest -BuildRequires: python3-setuptools -%description +%generate_buildrequires +%pyproject_buildrequires + +%global _description %{expand: This package contains a Python WSGI module for proxying KDC requests over HTTP by following the MS-KKDCP protocol. It aims to be simple to deploy, with minimal configuration. +} + +%description %{_description} %package -n python3-%{realname} Summary: MS-KKDCP (kerberos proxy) WSGI module @@ -34,30 +35,34 @@ Requires: python3-pyasn1 %{?python_provide:%python_provide python3-%{realname}} -%description -n python3-%{realname} -This package contains a Python 3.x WSGI module for proxying KDC requests over -HTTP by following the MS-KKDCP protocol. It aims to be simple to deploy, with -minimal configuration. +%description -n python3-%{realname} %{_description} %prep -%autosetup -S git -n %{realname}-%{version} +%autosetup -S git_am -n %{realname}-%{version} %build -%py3_build +%pyproject_wheel %install -%py3_install +%pyproject_install +%pyproject_save_files %{realname} %check -%{__python3} -m pytest +%pyproject_check_import +%pytest -%files -n python3-%{realname} +%files -n python%{python3_pkgversion}-%{realname} -f %{pyproject_files} %doc README %license COPYING -%{python3_sitelib}/%{realname}/ -%{python3_sitelib}/%{realname}-%{version}-*.egg-info %changelog +* Wed Nov 19 2025 Julien Rische - 1.1.0-1 +- New upstream version (1.1.0) +- Use DNS discovery for declared realms only (CVE-2025-59088) + Resolves: RHEL-113677 +- Fix DoS vulnerability based on unbounded TCP buffering (CVE-2025-59089) + Resolves: RHEL-113681 + * Fri Nov 22 2024 Julien Rische - 1.0.0-8 - Log KDC timeout only once per request Resolves: RHEL-68355 diff --git a/sources b/sources index 09d08fb..ca00ab8 100644 --- a/sources +++ b/sources @@ -1 +1,2 @@ -SHA512 (kdcproxy-1.0.0.tar.gz) = 617dba929d1c87c60d9a321269fd23348af11eabd8db3cea4b4750ec9514c9dce3487e658c396a5d009c9ef92326d45f5f00a2a116ab6469c62eda8270e55391 +SHA512 (kdcproxy-1.1.0.tar.gz) = f03b9d40d71322281d0197df6fe6b5936a8d09b0fee49fc5375b61974d005cedc5645f92a223d221c05c6ffd2613a86eb7d7295c4ac27a2f2c9eaa10fa24c182 +SHA512 (kdcproxy-1.1.0.tar.gz.sha512sum.txt) = b08dae1d80ef1c3dd2ff9a5da6411255860c229bc5da46d8ed85b3998f1d417c0b783912320d7e0d780956c117ad4c77699e5d68a61c44853014a5f985301fbd