Compare commits

..

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

16 changed files with 277 additions and 2219 deletions

1
.fmf/version Normal file
View File

@ -0,0 +1 @@
1

13
.gitignore vendored
View File

@ -1 +1,12 @@
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
/kdcproxy-1.1.0.tar.gz
/kdcproxy-1.1.0.tar.gz.sha512sum.txt

View File

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

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,71 +0,0 @@
From d66084656bd01e22a5f23119968bd939d380875c Mon Sep 17 00:00:00 2001
From: Julien Rische <jrische@redhat.com>
Date: Mon, 18 Nov 2024 10:01:16 +0100
Subject: [PATCH] Use dedicated "kdcproxy" logger
Signed-off-by: Julien Rische <jrische@redhat.com>
(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 cfec0f3..a84ad4a 100644
--- a/kdcproxy/__init__.py
+++ b/kdcproxy/__init__.py
@@ -37,6 +37,9 @@ else:
import httplib
import urlparse
+logging.basicConfig()
+logger = logging.getLogger('kdcproxy')
+
class HTTPException(Exception):
@@ -326,8 +329,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 5bff159..93af69a 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(filename)
except configparser.Error:
- logging.error("Unable to read config file: %s", filename)
+ logger.error("Unable to read config file: %s", filename)
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

View File

@ -1,158 +0,0 @@
From c8ed63653cfdf4a55ad4cf26cb11e8938b227f13 Mon Sep 17 00:00:00 2001
From: Julien Rische <jrische@redhat.com>
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 <jrische@redhat.com>
(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 9bc7044..cfec0f3 100644
--- a/kdcproxy/__init__.py
+++ b/kdcproxy/__init__.py
@@ -60,6 +60,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 = {
@@ -67,10 +74,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():
@@ -91,6 +111,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
@@ -100,8 +126,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)
@@ -109,7 +140,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)
@@ -117,6 +149,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):
@@ -214,6 +261,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)
@@ -260,6 +308,7 @@ class Application:
continue
except io.BlockingIOError:
pass
+ sockfno2addr[sock.fileno()] = addr
wsocks.append(sock)
# Resend packets to UDP servers
@@ -270,7 +319,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

View File

@ -1,227 +0,0 @@
%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}
Version: 0.4
Release: 5%{?dist}.2
Summary: MS-KKDCP (kerberos proxy) WSGI module
License: MIT
URL: https://github.com/npmccallum/%{realname}
Source0: https://github.com/npmccallum/%{realname}/archive/%{realname}-%{version}.tar.gz
Patch0: Make-webtest-an-optional-dependency.patch
Patch1: Correct-addrs-sorting-to-be-by-TCP-UDP.patch
Patch2: Always-buffer-TCP-data-in-__handle_recv.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
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-pytest
BuildRequires: python3-coverage
BuildRequires: python3-asn1crypto
BuildRequires: python3-dns
BuildRequires: python3-mock
%endif
%description
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.
%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}
Summary: MS-KKDCP (kerberos proxy) WSGI module
Requires: python3-dns
Requires: python3-asn1crypto
%{?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.
%endif
%prep
%autosetup -S git -n %{realname}-%{version}
%build
%if 0%{?with_python2} > 0
%py2_build
%endif
%if 0%{?with_python3} > 0
%py3_build
%endif
%install
%if 0%{?with_python2} > 0
%py2_install
%endif
%if 0%{?with_python3} > 0
%py3_install
%endif
%check
%if 0%{?with_python2} > 0
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}
%doc README
%license COPYING
%{python3_sitelib}/%{realname}/
%{python3_sitelib}/%{realname}-%{version}-*.egg-info
%endif
%changelog
* Wed Oct 22 2025 Julien Rische <jrische@redhat.com> - 0.4-5.2
- 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
Resolves: RHEL-68634
* Fri Oct 25 2019 Robbie Harwood <rharwood@redhat.com> - 0.4-5
- Always buffer TCP data in __handle_recv()
- Resolves: #1747144
* Fri Oct 25 2019 Robbie Harwood <rharwood@redhat.com> - 0.4-4
- Correct addrs sorting to be by TCP/UDP
- Resolves: #1732898
* Mon Nov 19 2018 Thomas Woerner <twoerner@redhat.com> - 0.4-3
- Bump release to be able to add python-kdcpoxy to the idm module
Resolves: RHBZ#1639332
* Thu Aug 09 2018 Robbie Harwood <rharwood@redhat.com> - 0.4-2
- Update dependencies in test suite
* Thu Aug 09 2018 Robbie Harwood <rharwood@redhat.com> - 0.4-1
- New upstream release - 0.4
- Port to autosetup
* Sat Jul 14 2018 Fedora Release Engineering <releng@fedoraproject.org> - 0.3.2-14
- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
* Tue Jun 19 2018 Miro Hrončok <mhroncok@redhat.com> - 0.3.2-13
- Rebuilt for Python 3.7
* Thu Mar 22 2018 Troy Dawson <tdawson@redhat.com> - 0.3.2-12
- Update conditionals.
- Make preperations for non-python2 builds
* Mon Feb 12 2018 Iryna Shcherbina <ishcherb@redhat.com> - 0.3.2-11
- Update Python 2 dependency declarations to new packaging standards
(See https://fedoraproject.org/wiki/FinalizingFedoraSwitchtoPython3)
* Fri Feb 09 2018 Fedora Release Engineering <releng@fedoraproject.org> - 0.3.2-10
- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
* Tue Sep 05 2017 Igor Gnatenko <ignatenko@redhat.com> - 0.3.2-9
- Ignore test results
* Thu Jul 27 2017 Fedora Release Engineering <releng@fedoraproject.org> - 0.3.2-8
- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild
* Sat Feb 11 2017 Fedora Release Engineering <releng@fedoraproject.org> - 0.3.2-7
- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
* Mon Jan 16 2017 Charalampos Stratakis <cstratak@redhat.com> - 0.3.2-6
- Fix failing tests
- Modernize the SPEC file
* Mon Dec 19 2016 Miro Hrončok <mhroncok@redhat.com> - 0.3.2-5
- Rebuild for Python 3.6
- BR /usr/bin/tox instead of python-tox
- Use %%{python3_version_nodots} instead of hardcoded 35
* Tue Jul 19 2016 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.3.2-4
- https://fedoraproject.org/wiki/Changes/Automatic_Provides_for_Python_RPM_Packages
* Thu Feb 04 2016 Fedora Release Engineering <releng@fedoraproject.org> - 0.3.2-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild
* Tue Nov 10 2015 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.3.2-2
- Rebuilt for https://fedoraproject.org/wiki/Changes/python3.5
* Mon Aug 03 2015 Nathaniel McCallum <npmccallum@fedoraproject.org> - 0.3.2-1
- Update to 0.3.2
- Fixes CVE-2015-5159
* Wed Jul 22 2015 Nathaniel McCallum <npmccallum@fedoraproject.org> - 0.3.1-1
- Update to 0.3.1
* Thu Jun 18 2015 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.3-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild
* Wed Jun 10 2015 Nathaniel McCallum <npmccallum@fedoraproject.org> - 0.3-1
- Update to 0.3
- Run tests in Fedora (not RHEL due to python-tox)
* Fri Oct 24 2014 Nathaniel McCallum <npmccallum@fedoraproject.org> - 0.2.1-1
- Update to 0.2.1
* Thu Oct 23 2014 Nathaniel McCallum <npmccallum@fedoraproject.org> - 0.2-1
- Update to 0.2
- Fix EPEL7 build
* Tue Jan 21 2014 Nathaniel McCallum <npmccallum@fedoraproject.org> - 0.1.1-1
- Initial package

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}

9
plans/tests.fmf Normal file
View File

@ -0,0 +1,9 @@
summary: Tests
discover:
how: fmf
dist-git-source: true
prepare:
how: feature
crb: enabled
execute:
how: tmt

233
python-kdcproxy.spec Normal file
View File

@ -0,0 +1,233 @@
%global realname kdcproxy
Name: python-%{realname}
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}/releases/download/v%{version}/%{realname}-%{version}.tar.gz
Source1: https://github.com/latchset/%{realname}/releases/download/v%{version}/%{realname}-%{version}.tar.gz.sha512sum.txt
# Patches
BuildArch: noarch
BuildRequires: git-core
BuildRequires: python3-pytest
%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
Requires: python3-dns
Requires: python3-pyasn1
%{?python_provide:%python_provide python3-%{realname}}
%description -n python3-%{realname} %{_description}
%prep
%autosetup -S git_am -n %{realname}-%{version}
%build
%pyproject_wheel
%install
%pyproject_install
%pyproject_save_files %{realname}
%check
%pyproject_check_import
%pytest
%files -n python%{python3_pkgversion}-%{realname} -f %{pyproject_files}
%doc README
%license COPYING
%changelog
* Wed Nov 19 2025 Julien Rische <jrische@redhat.com> - 1.1.0-1
- New upstream version (1.1.0)
- Use DNS discovery for declared realms only (CVE-2025-59088)
Resolves: RHEL-113652
- Fix DoS vulnerability based on unbounded TCP buffering (CVE-2025-59089)
Resolves: RHEL-113656
* Fri Nov 22 2024 Julien Rische <jrische@redhat.com> - 1.0.0-18
- Log KDC timeout only once per request
Resolves: RHEL-68354
* Tue Oct 29 2024 Troy Dawson <tdawson@redhat.com> - 1.0.0-17
- Bump release for October 2024 mass rebuild:
Resolves: RHEL-64018
* Mon Jun 24 2024 Troy Dawson <tdawson@redhat.com> - 1.0.0-16
- Bump release for June 2024 mass rebuild
* Fri Jan 26 2024 Fedora Release Engineering <releng@fedoraproject.org> - 1.0.0-15
- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild
* 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
- Update dependencies in test suite
* Thu Aug 09 2018 Robbie Harwood <rharwood@redhat.com> - 0.4-1
- New upstream release - 0.4
- Port to autosetup
* Sat Jul 14 2018 Fedora Release Engineering <releng@fedoraproject.org> - 0.3.2-14
- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
* Tue Jun 19 2018 Miro Hrončok <mhroncok@redhat.com> - 0.3.2-13
- Rebuilt for Python 3.7
* Thu Mar 22 2018 Troy Dawson <tdawson@redhat.com> - 0.3.2-12
- Update conditionals.
- Make preperations for non-python2 builds
* Mon Feb 12 2018 Iryna Shcherbina <ishcherb@redhat.com> - 0.3.2-11
- Update Python 2 dependency declarations to new packaging standards
(See https://fedoraproject.org/wiki/FinalizingFedoraSwitchtoPython3)
* Fri Feb 09 2018 Fedora Release Engineering <releng@fedoraproject.org> - 0.3.2-10
- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
* Tue Sep 05 2017 Igor Gnatenko <ignatenko@redhat.com> - 0.3.2-9
- Ignore test results
* Thu Jul 27 2017 Fedora Release Engineering <releng@fedoraproject.org> - 0.3.2-8
- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild
* Sat Feb 11 2017 Fedora Release Engineering <releng@fedoraproject.org> - 0.3.2-7
- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
* Mon Jan 16 2017 Charalampos Stratakis <cstratak@redhat.com> - 0.3.2-6
- Fix failing tests
- Modernize the SPEC file
* Mon Dec 19 2016 Miro Hrončok <mhroncok@redhat.com> - 0.3.2-5
- Rebuild for Python 3.6
- BR /usr/bin/tox instead of python-tox
- Use %%{python3_version_nodots} instead of hardcoded 35
* Tue Jul 19 2016 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.3.2-4
- https://fedoraproject.org/wiki/Changes/Automatic_Provides_for_Python_RPM_Packages
* Thu Feb 04 2016 Fedora Release Engineering <releng@fedoraproject.org> - 0.3.2-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild
* Tue Nov 10 2015 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.3.2-2
- Rebuilt for https://fedoraproject.org/wiki/Changes/python3.5
* Mon Aug 03 2015 Nathaniel McCallum <npmccallum@fedoraproject.org> - 0.3.2-1
- Update to 0.3.2
- Fixes CVE-2015-5159
* Wed Jul 22 2015 Nathaniel McCallum <npmccallum@fedoraproject.org> - 0.3.1-1
- Update to 0.3.1
* Thu Jun 18 2015 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.3-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild
* Wed Jun 10 2015 Nathaniel McCallum <npmccallum@fedoraproject.org> - 0.3-1
- Update to 0.3
- Run tests in Fedora (not RHEL due to python-tox)
* Fri Oct 24 2014 Nathaniel McCallum <npmccallum@fedoraproject.org> - 0.2.1-1
- Update to 0.2.1
* Thu Oct 23 2014 Nathaniel McCallum <npmccallum@fedoraproject.org> - 0.2-1
- Update to 0.2
- Fix EPEL7 build
* Tue Jan 21 2014 Nathaniel McCallum <npmccallum@fedoraproject.org> - 0.1.1-1
- Initial package

2
sources Normal file
View File

@ -0,0 +1,2 @@
SHA512 (kdcproxy-1.1.0.tar.gz) = f03b9d40d71322281d0197df6fe6b5936a8d09b0fee49fc5375b61974d005cedc5645f92a223d221c05c6ffd2613a86eb7d7295c4ac27a2f2c9eaa10fa24c182
SHA512 (kdcproxy-1.1.0.tar.gz.sha512sum.txt) = b08dae1d80ef1c3dd2ff9a5da6411255860c229bc5da46d8ed85b3998f1d417c0b783912320d7e0d780956c117ad4c77699e5d68a61c44853014a5f985301fbd

13
tests/unittests/main.fmf Normal file
View File

@ -0,0 +1,13 @@
summary: Unit tests
component:
- python-kdcproxy
require:
- python3-kdcproxy
- python3-pytest
test: |
setuppy_path="$(find "$TMT_SOURCE_DIR" -type f -name setup.py)"
src_path="$(dirname "$setuppy_path")"
pushd "$src_path"
python3 -m pytest
duration: 15m
enabled: true