Compare commits

..

No commits in common. "c8-stream-DL1" and "c10s" have entirely different histories.

14 changed files with 265 additions and 1882 deletions

11
.gitignore vendored
View File

@ -1 +1,10 @@
SOURCES/kdcproxy-0.4.tar.gz /kdcproxy-0.1.1.tar.gz
/kdcproxy-0.2.tar.gz
/kdcproxy-0.2.1.tar.gz
/v0.3.tar.gz
/v0.3.1.tar.gz
/v0.3.2.tar.gz
/kdcproxy-0.4.tar.gz
/kdcproxy-0.4.1.tar.gz
/kdcproxy-0.4.2.tar.gz
/kdcproxy-1.0.0.tar.gz

View File

@ -1 +0,0 @@
48cffec358fe9e15a66fb040b6b7fc87f642f6da SOURCES/kdcproxy-0.4.tar.gz

View File

@ -0,0 +1,100 @@
From 7b7aee01d72be5a310678cdad189cb7382f28549 Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
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 <pviktori@redhat.com>
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
(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

View File

@ -1,206 +0,0 @@
From 829cfae65bde395302948c99dbf0e0e8f97a1ce0 Mon Sep 17 00:00:00 2001
From: Julien Rische <jrische@redhat.com>
Date: Fri, 3 Oct 2025 17:39:36 +0200
Subject: [PATCH] Fix DoS vulnerability based on unbounded TCP buffering
In Application.__handle_recv(), the next part of the TCP exchange is
received and queued to the io.BytesIO stream. Then, the content of the
stream was systematically exported to a buffer. However, this buffer
is only used if the data transfer is finished, causing a waste of
processing resources if the message is received in multiple parts.
On top of these unnecessary operations, this function does not handle
length limits properly: it accepts to receive chunks of data with both
an individual and total length larger than the maximum theoretical
length of a Kerberos message, and will continue to wait for data as long
as the input stream's length is not exactly the same as the one provided
in the header of the response (even if the stream is already longer than
the expected length).
If the kdcproxy service is not protected against DNS discovery abuse,
the attacker could take advantage of these problems to operate a
denial-of-service attack (CVE-2025-59089).
After this commit, kdcproxy will interrupt the receiving of a message
after it exceeds the maximum length of a Kerberos message or the length
indicated in the message header. Also it will only export the content of
the input stream to a buffer once the receiving process has ended.
Signed-off-by: Julien Rische <jrische@redhat.com>
(cherry picked from commit 93ba7376098d9a3b6d039475e15778b0ffd024de)
---
kdcproxy/__init__.py | 51 +++++++++++++++++++-------------
tests.py | 70 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 100 insertions(+), 21 deletions(-)
diff --git a/kdcproxy/__init__.py b/kdcproxy/__init__.py
index a84ad4a..10a7b00 100644
--- a/kdcproxy/__init__.py
+++ b/kdcproxy/__init__.py
@@ -148,6 +148,7 @@ class Application:
if self.sock_type(sock) == socket.SOCK_STREAM:
# Remove broken TCP socket from readers
rsocks.remove(sock)
+ read_buffers.pop(sock)
else:
if reply is not None:
return reply
@@ -173,7 +174,7 @@ class Application:
if self.sock_type(sock) == socket.SOCK_DGRAM:
# For UDP sockets, recv() returns an entire datagram
# package. KDC sends one datagram as reply.
- reply = sock.recv(1048576)
+ reply = sock.recv(self.MAX_LENGTH)
# If we proxy over UDP, we will be missing the 4-byte
# length prefix. So add it.
reply = struct.pack("!I", len(reply)) + reply
@@ -185,30 +186,38 @@ class Application:
if buf is None:
read_buffers[sock] = buf = io.BytesIO()
- part = sock.recv(1048576)
- if not part:
- # EOF received. Return any incomplete data we have on the theory
- # that a decode error is more apparent than silent failure. The
- # client will fail faster, at least.
- read_buffers.pop(sock)
- reply = buf.getvalue()
- return reply
+ part = sock.recv(self.MAX_LENGTH)
+ if part:
+ # Data received, accumulate it in a buffer.
+ buf.write(part)
- # Data received, accumulate it in a buffer.
- buf.write(part)
+ reply = buf.getbuffer()
+ if len(reply) < 4:
+ # We don't have the length yet.
+ return None
- reply = buf.getvalue()
- if len(reply) < 4:
- # We don't have the length yet.
- return None
+ # Got enough data to check if we have the full package.
+ (length, ) = struct.unpack("!I", reply[0:4])
+ length += 4 # add prefix length
- # Got enough data to check if we have the full package.
- (length, ) = struct.unpack("!I", reply[0:4])
- if length + 4 == len(reply):
- read_buffers.pop(sock)
- return reply
+ if length > self.MAX_LENGTH:
+ raise ValueError('Message length exceeds the maximum length '
+ 'for a Kerberos message (%i > %i)'
+ % (length, self.MAX_LENGTH))
- return None
+ if len(reply) > length:
+ raise ValueError('Message length exceeds its expected length '
+ '(%i > %i)' % (len(reply), length))
+
+ if len(reply) < length:
+ return None
+
+ # Else (if part is None), EOF was received. Return any incomplete data
+ # we have on the theory that a decode error is more apparent than
+ # silent failure. The client will fail faster, at least.
+
+ read_buffers.pop(sock)
+ return buf.getvalue()
def __filter_addr(self, addr):
if addr[0] not in (socket.AF_INET, socket.AF_INET6):
diff --git a/tests.py b/tests.py
index c2b1fc0..e3fa4f7 100644
--- a/tests.py
+++ b/tests.py
@@ -20,6 +20,8 @@
# THE SOFTWARE.
import os
+import socket
+import struct
import sys
import unittest
from base64 import b64decode
@@ -123,6 +125,74 @@ class KDCProxyWSGITests(unittest.TestCase):
kpasswd=True)
self.assertEqual(response.status_code, 503)
+ @mock.patch("socket.getaddrinfo", return_value=addrinfo)
+ @mock.patch("socket.socket")
+ def test_tcp_message_length_exceeds_max(self, m_socket, m_getaddrinfo):
+ # Test that TCP messages with length > MAX_LENGTH raise ValueError
+ # Create a message claiming to be larger than MAX_LENGTH
+ max_len = self.app.MAX_LENGTH
+ # Length prefix claiming message is larger than allowed
+ oversized_length = max_len + 1
+ malicious_msg = struct.pack("!I", oversized_length)
+
+ # Mock socket to return the malicious length prefix
+ mock_sock = m_socket.return_value
+ mock_sock.recv.return_value = malicious_msg
+ mock_sock.getsockopt.return_value = socket.SOCK_STREAM
+
+ # Manually call the receive method to test it
+ read_buffers = {}
+ with self.assertRaises(ValueError) as cm:
+ self.app._Application__handle_recv(mock_sock, read_buffers)
+
+ self.assertIn("exceeds the maximum length", str(cm.exception))
+ self.assertIn(str(max_len), str(cm.exception))
+
+ @mock.patch("socket.getaddrinfo", return_value=addrinfo)
+ @mock.patch("socket.socket")
+ def test_tcp_message_data_exceeds_expected_length(
+ self, m_socket, m_getaddrinfo
+ ):
+ # Test that receiving more data than expected raises ValueError
+ # Create a message with length = 100 but send more data
+ expected_length = 100
+ length_prefix = struct.pack("!I", expected_length)
+ # Send more data than the length prefix indicates
+ extra_data = b"X" * (expected_length + 10)
+ malicious_msg = length_prefix + extra_data
+
+ mock_sock = m_socket.return_value
+ mock_sock.recv.return_value = malicious_msg
+ mock_sock.getsockopt.return_value = socket.SOCK_STREAM
+
+ read_buffers = {}
+ with self.assertRaises(ValueError) as cm:
+ self.app._Application__handle_recv(mock_sock, read_buffers)
+
+ self.assertIn("exceeds its expected length", str(cm.exception))
+
+ @mock.patch("socket.getaddrinfo", return_value=addrinfo)
+ @mock.patch("socket.socket")
+ def test_tcp_eof_returns_buffered_data(self, m_socket, m_getaddrinfo):
+ # Test that EOF returns any buffered data
+ initial_data = b"\x00\x00\x00\x10" # Length = 16
+ mock_sock = m_socket.return_value
+ mock_sock.getsockopt.return_value = socket.SOCK_STREAM
+
+ # First recv returns some data, second returns empty (EOF)
+ mock_sock.recv.side_effect = [initial_data, b""]
+
+ read_buffers = {}
+ # First call buffers the data
+ result = self.app._Application__handle_recv(mock_sock, read_buffers)
+ self.assertIsNone(result) # Not complete yet
+
+ # Second call gets EOF and returns buffered data
+ result = self.app._Application__handle_recv(mock_sock, read_buffers)
+ self.assertEqual(result, initial_data)
+ # Buffer should be cleaned up
+ self.assertNotIn(mock_sock, read_buffers)
+
def decode(data):
data = data.replace(b'\\n', b'')
--
2.51.0

File diff suppressed because it is too large Load Diff

View File

@ -1,84 +0,0 @@
From c1be487bb00f2e813212031d93fcebbfbd0da60b Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Thu, 29 Aug 2019 11:13:41 -0400
Subject: [PATCH] Always buffer TCP data in __handle_recv()
Refactor __handle_recv() to always create a BytesIO() object for TCP
data. Linearize control flow for ease of debugging. Always apply
length checks so that we don't have to wait for EOF in the multiple-recv
case.
Fixes a bug where we wouldn't return any data because we never received
the EOF, or didn't receive it fast enough.
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
(cherry picked from commit 7e2b1ab27b843c220fe301b74bab01ed61b0f59a)
---
kdcproxy/__init__.py | 54 +++++++++++++++++++++++++-------------------
1 file changed, 31 insertions(+), 23 deletions(-)
diff --git a/kdcproxy/__init__.py b/kdcproxy/__init__.py
index 6526bc9..9bc7044 100644
--- a/kdcproxy/__init__.py
+++ b/kdcproxy/__init__.py
@@ -128,29 +128,37 @@ class Application:
# length prefix. So add it.
reply = struct.pack("!I", len(reply)) + reply
return reply
- else:
- # TCP is a different story. The reply must be buffered
- # until the full answer is accumulated.
- buf = read_buffers.get(sock)
- part = sock.recv(1048576)
- if buf is None:
- if len(part) > 4:
- # got enough data in the initial package. Now check
- # if we got the full package in the first run.
- (length, ) = struct.unpack("!I", part[0:4])
- if length + 4 == len(part):
- return part
- read_buffers[sock] = buf = io.BytesIO()
-
- if part:
- # data received, accumulate it in a buffer
- buf.write(part)
- return None
- else:
- # EOF received
- read_buffers.pop(sock)
- reply = buf.getvalue()
- return reply
+
+ # TCP is a different story. The reply must be buffered until the full
+ # answer is accumulated.
+ buf = read_buffers.get(sock)
+ if buf is None:
+ read_buffers[sock] = buf = io.BytesIO()
+
+ part = sock.recv(1048576)
+ if not part:
+ # EOF received. Return any incomplete data we have on the theory
+ # that a decode error is more apparent than silent failure. The
+ # client will fail faster, at least.
+ read_buffers.pop(sock)
+ reply = buf.getvalue()
+ return reply
+
+ # Data received, accumulate it in a buffer.
+ buf.write(part)
+
+ reply = buf.getvalue()
+ if len(reply) < 4:
+ # We don't have the length yet.
+ return None
+
+ # Got enough data to check if we have the full package.
+ (length, ) = struct.unpack("!I", reply[0:4])
+ if length + 4 == len(reply):
+ read_buffers.pop(sock)
+ return reply
+
+ return None
def __filter_addr(self, addr):
if addr[0] not in (socket.AF_INET, socket.AF_INET6):

View File

@ -1,28 +0,0 @@
From 5cfde6d085320da3fb5d4c6506e6d6253438669c Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Fri, 2 Aug 2019 13:54:05 -0400
Subject: [PATCH] Correct addrs sorting to be by TCP/UDP
Fixes any potential cases where the resolver might yield UDP addresses
first.
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
(cherry picked from commit d0b35c2b71a172f409b4311d36538d8fa3433c58)
---
kdcproxy/__init__.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/kdcproxy/__init__.py b/kdcproxy/__init__.py
index c59f2b3..6526bc9 100644
--- a/kdcproxy/__init__.py
+++ b/kdcproxy/__init__.py
@@ -227,7 +227,8 @@ class Application:
#
# Stick a None address on the end so we can get one
# more attempt after all servers have been contacted.
- addrs = tuple(sorted(filter(self.__filter_addr, addrs)))
+ addrs = tuple(sorted(filter(self.__filter_addr, addrs),
+ key=lambda a: a[2]))
for addr in addrs + (None,):
if addr is not None:
# Bypass unspecified socktypes

View File

@ -1,36 +0,0 @@
From 2164f10fe5d992006f42c4a8d682f23b04ffbf12 Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Thu, 9 Aug 2018 14:57:56 -0400
Subject: [PATCH] Make webtest an optional dependency
Resolves: #38
(cherry picked from commit c0bee88c60deb176d420d90447d24c370d70727a)
---
tests.py | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/tests.py b/tests.py
index 992529f..c2b1fc0 100644
--- a/tests.py
+++ b/tests.py
@@ -33,7 +33,11 @@ from dns.rdataclass import IN as RDCLASS_IN
from dns.rdatatype import SRV as RDTYPE_SRV
from dns.rdtypes.IN.SRV import SRV
-from webtest import TestApp as WebTestApp
+try:
+ from webtest import TestApp as WebTestApp
+except ImportError:
+ print("webtest not installed! Tests will be skipped")
+ WebTestApp = "skip"
import kdcproxy
from kdcproxy import codec
@@ -45,6 +49,7 @@ HERE = os.path.dirname(os.path.abspath(__file__))
KRB5_CONFIG = os.path.join(HERE, 'tests.krb5.conf')
+@unittest.skipIf(WebTestApp == "skip", "webtest not installed")
class KDCProxyWSGITests(unittest.TestCase):
addrinfo = [
(2, 1, 6, '', ('128.66.0.2', 88)),

View File

@ -1,4 +1,4 @@
From d66084656bd01e22a5f23119968bd939d380875c Mon Sep 17 00:00:00 2001 From 99babf4ba3ce4d1f5bb893e7678df44d16b74d03 Mon Sep 17 00:00:00 2001
From: Julien Rische <jrische@redhat.com> From: Julien Rische <jrische@redhat.com>
Date: Mon, 18 Nov 2024 10:01:16 +0100 Date: Mon, 18 Nov 2024 10:01:16 +0100
Subject: [PATCH] Use dedicated "kdcproxy" logger Subject: [PATCH] Use dedicated "kdcproxy" logger
@ -11,10 +11,10 @@ Signed-off-by: Julien Rische <jrische@redhat.com>
2 files changed, 10 insertions(+), 4 deletions(-) 2 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/kdcproxy/__init__.py b/kdcproxy/__init__.py diff --git a/kdcproxy/__init__.py b/kdcproxy/__init__.py
index cfec0f3..a84ad4a 100644 index d0ca43e..ce96a0c 100644
--- a/kdcproxy/__init__.py --- a/kdcproxy/__init__.py
+++ b/kdcproxy/__init__.py +++ b/kdcproxy/__init__.py
@@ -37,6 +37,9 @@ else: @@ -38,6 +38,9 @@ else:
import httplib import httplib
import urlparse import urlparse
@ -24,7 +24,7 @@ index cfec0f3..a84ad4a 100644
class HTTPException(Exception): class HTTPException(Exception):
@@ -326,8 +329,8 @@ class Application: @@ -327,8 +330,8 @@ class Application:
fail_socktype = self.addr2socktypename(fail_addr) fail_socktype = self.addr2socktypename(fail_addr)
fail_ip = fail_addr[4][0] fail_ip = fail_addr[4][0]
fail_port = fail_addr[4][1] fail_port = fail_addr[4][1]
@ -36,7 +36,7 @@ index cfec0f3..a84ad4a 100644
break break
diff --git a/kdcproxy/config/__init__.py b/kdcproxy/config/__init__.py diff --git a/kdcproxy/config/__init__.py b/kdcproxy/config/__init__.py
index 5bff159..93af69a 100644 index a1435b7..8e17c5b 100644
--- a/kdcproxy/config/__init__.py --- a/kdcproxy/config/__init__.py
+++ b/kdcproxy/config/__init__.py +++ b/kdcproxy/config/__init__.py
@@ -32,6 +32,9 @@ except ImportError: # Python 2.x @@ -32,6 +32,9 @@ except ImportError: # Python 2.x
@ -51,10 +51,10 @@ index 5bff159..93af69a 100644
@@ -60,14 +63,14 @@ class KDCProxyConfig(IConfig): @@ -60,14 +63,14 @@ class KDCProxyConfig(IConfig):
try: try:
self.__cp.read(filename) self.__cp.read(filenames)
except configparser.Error: except configparser.Error:
- logging.error("Unable to read config file: %s", filename) - logging.error("Unable to read config file(s): %s", filenames)
+ logger.error("Unable to read config file: %s", filename) + logger.error("Unable to read config file(s): %s", filenames)
try: try:
mod = self.__cp.get(self.GLOBAL, "configs") mod = self.__cp.get(self.GLOBAL, "configs")

View File

@ -1,4 +1,4 @@
From c8ed63653cfdf4a55ad4cf26cb11e8938b227f13 Mon Sep 17 00:00:00 2001 From 0b2efa7b2901ada01758a0525a21af5447aa647a Mon Sep 17 00:00:00 2001
From: Julien Rische <jrische@redhat.com> From: Julien Rische <jrische@redhat.com>
Date: Mon, 18 Nov 2024 09:38:13 +0100 Date: Mon, 18 Nov 2024 09:38:13 +0100
Subject: [PATCH] Use exponential backoff for connection retries Subject: [PATCH] Use exponential backoff for connection retries
@ -19,10 +19,10 @@ Signed-off-by: Julien Rische <jrische@redhat.com>
1 file changed, 60 insertions(+), 3 deletions(-) 1 file changed, 60 insertions(+), 3 deletions(-)
diff --git a/kdcproxy/__init__.py b/kdcproxy/__init__.py diff --git a/kdcproxy/__init__.py b/kdcproxy/__init__.py
index 9bc7044..cfec0f3 100644 index 1493b30..d0ca43e 100644
--- a/kdcproxy/__init__.py --- a/kdcproxy/__init__.py
+++ b/kdcproxy/__init__.py +++ b/kdcproxy/__init__.py
@@ -60,6 +60,13 @@ class HTTPException(Exception): @@ -61,6 +61,13 @@ class HTTPException(Exception):
return "%d %s" % (self.code, httplib.responses[self.code]) return "%d %s" % (self.code, httplib.responses[self.code])
@ -36,7 +36,7 @@ index 9bc7044..cfec0f3 100644
class Application: class Application:
MAX_LENGTH = 128 * 1024 MAX_LENGTH = 128 * 1024
SOCKTYPES = { SOCKTYPES = {
@@ -67,10 +74,23 @@ class Application: @@ -68,10 +75,23 @@ class Application:
"udp": socket.SOCK_DGRAM, "udp": socket.SOCK_DGRAM,
} }
@ -60,7 +60,7 @@ index 9bc7044..cfec0f3 100644
extra = 0 extra = 0
read_buffers = {} read_buffers = {}
while (timeout + extra) > time.time(): while (timeout + extra) > time.time():
@@ -91,6 +111,12 @@ class Application: @@ -92,6 +112,12 @@ class Application:
pass pass
for sock in w: for sock in w:
@ -73,7 +73,7 @@ index 9bc7044..cfec0f3 100644
try: try:
if self.sock_type(sock) == socket.SOCK_DGRAM: if self.sock_type(sock) == socket.SOCK_DGRAM:
# If we proxy over UDP, remove the 4-byte length # If we proxy over UDP, remove the 4-byte length
@@ -100,8 +126,13 @@ class Application: @@ -101,8 +127,13 @@ class Application:
sock.sendall(pr.request) sock.sendall(pr.request)
extra = 10 # New connections get 10 extra seconds extra = 10 # New connections get 10 extra seconds
except Exception as e: except Exception as e:
@ -88,7 +88,7 @@ index 9bc7044..cfec0f3 100644
rsocks.append(sock) rsocks.append(sock)
wsocks.remove(sock) wsocks.remove(sock)
@@ -109,7 +140,8 @@ class Application: @@ -110,7 +141,8 @@ class Application:
try: try:
reply = self.__handle_recv(sock, read_buffers) reply = self.__handle_recv(sock, read_buffers)
except Exception as e: except Exception as e:
@ -98,7 +98,7 @@ index 9bc7044..cfec0f3 100644
if self.sock_type(sock) == socket.SOCK_STREAM: if self.sock_type(sock) == socket.SOCK_STREAM:
# Remove broken TCP socket from readers # Remove broken TCP socket from readers
rsocks.remove(sock) rsocks.remove(sock)
@@ -117,6 +149,21 @@ class Application: @@ -118,6 +150,21 @@ class Application:
if reply is not None: if reply is not None:
return reply return reply
@ -120,7 +120,7 @@ index 9bc7044..cfec0f3 100644
return None return None
def __handle_recv(self, sock, read_buffers): def __handle_recv(self, sock, read_buffers):
@@ -214,6 +261,7 @@ class Application: @@ -215,6 +262,7 @@ class Application:
reply = None reply = None
wsocks = [] wsocks = []
rsocks = [] rsocks = []
@ -128,7 +128,7 @@ index 9bc7044..cfec0f3 100644
for server in map(urlparse.urlparse, servers): for server in map(urlparse.urlparse, servers):
# Enforce valid, supported URIs # Enforce valid, supported URIs
scheme = server.scheme.lower().split("+", 1) scheme = server.scheme.lower().split("+", 1)
@@ -260,6 +308,7 @@ class Application: @@ -261,6 +309,7 @@ class Application:
continue continue
except io.BlockingIOError: except io.BlockingIOError:
pass pass
@ -136,7 +136,7 @@ index 9bc7044..cfec0f3 100644
wsocks.append(sock) wsocks.append(sock)
# Resend packets to UDP servers # Resend packets to UDP servers
@@ -270,7 +319,15 @@ class Application: @@ -271,7 +320,15 @@ class Application:
# Call select() # Call select()
timeout = time.time() + (15 if addr is None else 2) timeout = time.time() + (15 if addr is None else 2)

7
gating.yaml Normal file
View File

@ -0,0 +1,7 @@
# recipients: abokovoy, frenaud, kaleem, ftrivino, cheimes
--- !Policy
product_versions:
- rhel-10
decision_context: osci_compose_gate
rules:
- !PassingTestCaseRule {test_case_name: osci.brew-build.tier0.functional}

View File

@ -1,80 +1,36 @@
%global realname kdcproxy %global realname kdcproxy
%if 0%{?fedora} || 0%{?rhel} > 7
%global with_python3 1
%else
%global with_python3 0
%endif
%if 0%{?fedora} || 0%{?rhel} <= 7
%global with_python2 1
%else
%global with_python2 0
%endif
Name: python-%{realname} Name: python-%{realname}
Version: 0.4 Version: 1.0.0
Release: 5%{?dist}.2 Release: 18%{?dist}
Summary: MS-KKDCP (kerberos proxy) WSGI module Summary: MS-KKDCP (kerberos proxy) WSGI module
License: MIT License: MIT
URL: https://github.com/npmccallum/%{realname} URL: https://github.com/latchset/%{realname}
Source0: https://github.com/npmccallum/%{realname}/archive/%{realname}-%{version}.tar.gz Source0: https://github.com/latchset/%{realname}/archive/%{realname}-%{version}.tar.gz
Patch0: Make-webtest-an-optional-dependency.patch Patch0: Drop-coverage-from-tests.patch
Patch1: Correct-addrs-sorting-to-be-by-TCP-UDP.patch Patch1: Use-exponential-backoff-for-connection-retries.patch
Patch2: Always-buffer-TCP-data-in-__handle_recv.patch Patch2: Use-dedicated-kdcproxy-logger.patch
Patch3: Use-exponential-backoff-for-connection-retries.patch
Patch4: Use-dedicated-kdcproxy-logger.patch
Patch5: 0005-Fix-DoS-vulnerability-based-on-unbounded-TCP-bufferi.patch
Patch6: 0006-Use-DNS-discovery-for-declared-realms-only.patch
BuildArch: noarch BuildArch: noarch
BuildRequires: git BuildRequires: git
%if 0%{?with_python2} > 0
BuildRequires: python2-devel
BuildRequires: python2-pytest
BuildRequires: python2-coverage
BuildRequires: python2-asn1crypto
BuildRequires: python2-dns
BuildRequires: python2-mock
%endif
%if 0%{?with_python3} > 0
BuildRequires: python3-devel BuildRequires: python3-devel
BuildRequires: python3-pytest
BuildRequires: python3-coverage
BuildRequires: python3-asn1crypto
BuildRequires: python3-dns BuildRequires: python3-dns
BuildRequires: python3-mock BuildRequires: python3-pyasn1
%endif BuildRequires: python3-pytest
BuildRequires: python3-setuptools
%description %description
This package contains a Python WSGI module for proxying KDC requests over 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 HTTP by following the MS-KKDCP protocol. It aims to be simple to deploy, with
minimal configuration. minimal configuration.
%if 0%{?with_python2} > 0
%package -n python2-%{realname}
Summary: MS-KKDCP (kerberos proxy) WSGI module
Requires: python2-dns
Requires: python2-asn1crypto
%{?python_provide:%python_provide python2-%{realname}}
%description -n python2-%{realname}
This package contains a Python 2.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.
%endif
%if 0%{?with_python3} > 0
%package -n python3-%{realname} %package -n python3-%{realname}
Summary: MS-KKDCP (kerberos proxy) WSGI module Summary: MS-KKDCP (kerberos proxy) WSGI module
Requires: python3-dns Requires: python3-dns
Requires: python3-asn1crypto Requires: python3-pyasn1
%{?python_provide:%python_provide python3-%{realname}} %{?python_provide:%python_provide python3-%{realname}}
@ -82,74 +38,119 @@ Requires: python3-asn1crypto
This package contains a Python 3.x WSGI module for proxying KDC requests over 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 HTTP by following the MS-KKDCP protocol. It aims to be simple to deploy, with
minimal configuration. minimal configuration.
%endif
%prep %prep
%autosetup -S git -n %{realname}-%{version} %autosetup -S git -n %{realname}-%{version}
%build %build
%if 0%{?with_python2} > 0
%py2_build
%endif
%if 0%{?with_python3} > 0
%py3_build %py3_build
%endif
%install %install
%if 0%{?with_python2} > 0
%py2_install
%endif
%if 0%{?with_python3} > 0
%py3_install %py3_install
%endif
%check %check
%if 0%{?with_python2} > 0 %{__python3} -m pytest
KDCPROXY_ASN1MOD=asn1crypto %{__python2} -m pytest
%endif
%if 0%{?with_python3} > 0
KDCPROXY_ASN1MOD=asn1crypto %{__python3} -m pytest
%endif
%if 0%{?with_python2} > 0
%files -n python2-%{realname}
%doc README
%license COPYING
%{python2_sitelib}/%{realname}/
%{python2_sitelib}/%{realname}-%{version}-*.egg-info
%endif
%if 0%{?with_python3} > 0
%files -n python3-%{realname} %files -n python3-%{realname}
%doc README %doc README
%license COPYING %license COPYING
%{python3_sitelib}/%{realname}/ %{python3_sitelib}/%{realname}/
%{python3_sitelib}/%{realname}-%{version}-*.egg-info %{python3_sitelib}/%{realname}-%{version}-*.egg-info
%endif
%changelog %changelog
* Wed Oct 22 2025 Julien Rische <jrische@redhat.com> - 0.4-5.2 * Fri Nov 22 2024 Julien Rische <jrische@redhat.com> - 1.0.0-18
- Use DNS discovery for declared realms only (CVE-2025-59088)
Resolves: RHEL-113657
- Fix DoS vulnerability based on unbounded TCP buffering (CVE-2025-59089)
Resolves: RHEL-113664
* Fri Nov 22 2024 Julien Rische <jrische@redhat.com> - 0.4-5.1
- Log KDC timeout only once per request - Log KDC timeout only once per request
Resolves: RHEL-68634 Resolves: RHEL-68354
* Fri Oct 25 2019 Robbie Harwood <rharwood@redhat.com> - 0.4-5 * Tue Oct 29 2024 Troy Dawson <tdawson@redhat.com> - 1.0.0-17
- Always buffer TCP data in __handle_recv() - Bump release for October 2024 mass rebuild:
- Resolves: #1747144 Resolves: RHEL-64018
* Fri Oct 25 2019 Robbie Harwood <rharwood@redhat.com> - 0.4-4 * Mon Jun 24 2024 Troy Dawson <tdawson@redhat.com> - 1.0.0-16
- Correct addrs sorting to be by TCP/UDP - Bump release for June 2024 mass rebuild
- Resolves: #1732898
* Mon Nov 19 2018 Thomas Woerner <twoerner@redhat.com> - 0.4-3 * Fri Jan 26 2024 Fedora Release Engineering <releng@fedoraproject.org> - 1.0.0-15
- Bump release to be able to add python-kdcpoxy to the idm module - Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild
Resolves: RHBZ#1639332
* Mon Jan 22 2024 Fedora Release Engineering <releng@fedoraproject.org> - 1.0.0-14
- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild
* Fri Jul 21 2023 Fedora Release Engineering <releng@fedoraproject.org> - 1.0.0-13
- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild
* Thu Jun 15 2023 Python Maint <python-maint@redhat.com> - 1.0.0-12
- Rebuilt for Python 3.12
* Fri Jan 20 2023 Fedora Release Engineering <releng@fedoraproject.org> - 1.0.0-11
- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild
* Fri Jul 22 2022 Fedora Release Engineering <releng@fedoraproject.org> - 1.0.0-10
- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild
* Tue Jun 14 2022 Python Maint <python-maint@redhat.com> - 1.0.0-9
- Rebuilt for Python 3.11
* Fri Jan 21 2022 Fedora Release Engineering <releng@fedoraproject.org> - 1.0.0-8
- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild
* Fri Jul 23 2021 Fedora Release Engineering <releng@fedoraproject.org> - 1.0.0-7
- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild
* Fri Jun 04 2021 Python Maint <python-maint@redhat.com> - 1.0.0-6
- Rebuilt for Python 3.10
* Thu Apr 08 2021 Robbie Harwood <rharwood@redhat.com> - 1.0.0-5
- Actually drop coverage dependency
* Fri Jan 29 2021 Robbie Harwood <rharwood@redhat.com> - 1.0.0-4
- Drop unused dependency on python3-mock
- Resolves: #1922344
* Wed Jan 27 2021 Fedora Release Engineering <releng@fedoraproject.org> - 1.0.0-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
* Wed Jan 20 2021 Robbie Harwood <rharwood@redhat.com> - 1.0.0-2
- Drop coverage from tests
- Resolves: #1916739
* Tue Dec 08 2020 Robbie Harwood <rharwood@redhat.com> - 1.0.0-1
- New upstream version (1.0.0)
- Drop asn1crypto in favor of pyasn1
* Wed Jul 29 2020 Fedora Release Engineering <releng@fedoraproject.org> - 0.4.2-6
- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
* Thu Jun 25 2020 Robbie Harwood <rharwood@redhat.com> - 0.4.2-5
- Explicitly depend on python3-setuptools
* Tue May 26 2020 Miro Hrončok <mhroncok@redhat.com> - 0.4.2-4
- Rebuilt for Python 3.9
* Thu Jan 30 2020 Fedora Release Engineering <releng@fedoraproject.org> - 0.4.2-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild
* Thu Oct 03 2019 Miro Hrončok <mhroncok@redhat.com> - 0.4.2-2
- Rebuilt for Python 3.8.0rc1 (#1748018)
* Thu Aug 29 2019 Robbie Harwood <rharwood@redhat.com> - 0.4.2-1
- New upstream version (0.4.2)
* Mon Aug 19 2019 Miro Hrončok <mhroncok@redhat.com> - 0.4.1-3
- Rebuilt for Python 3.8
* Fri Jul 26 2019 Fedora Release Engineering <releng@fedoraproject.org> - 0.4.1-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild
* Mon Feb 11 2019 Robbie Harwood <rharwood@redhdat.com> 0.4.1-1
- New upstream release - 0.4.1
- Fixes build with rawhide (dnspython/dnspython3)
* Sat Feb 02 2019 Fedora Release Engineering <releng@fedoraproject.org> - 0.4-4
- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
* Mon Sep 17 2018 Robbie Harwood <rharwood@redhat.com> - 0.4-3
- Drop python2 subpackage
- Resolves: #1629775
* Thu Aug 09 2018 Robbie Harwood <rharwood@redhat.com> - 0.4-2 * Thu Aug 09 2018 Robbie Harwood <rharwood@redhat.com> - 0.4-2
- Update dependencies in test suite - Update dependencies in test suite

1
sources Normal file
View File

@ -0,0 +1 @@
SHA512 (kdcproxy-1.0.0.tar.gz) = 617dba929d1c87c60d9a321269fd23348af11eabd8db3cea4b4750ec9514c9dce3487e658c396a5d009c9ef92326d45f5f00a2a116ab6469c62eda8270e55391

27
tests/tests.yml Normal file
View File

@ -0,0 +1,27 @@
---
- hosts: localhost
tags:
- classic
pre_tasks:
- name: Enable CRB for python3-pytest on 1minutetip
ini_file:
path: /etc/yum.repos.d/rhel.repo
section: rhel-CRB
option: enabled
value: "1"
create: no
ignore_errors: yes
roles:
- role: standard-test-source
required_packages:
- git
- role: standard-test-basic
required_packages:
- python3-kdcproxy
- python3-pytest
tests:
- unittests:
dir: "source"
run: >-
rm -rf kdcproxy* &&
python3 -m pytest