diff --git a/Address-DoS-via-the-Tudoor-mechanism-CVE-2023-29483-.patch b/Address-DoS-via-the-Tudoor-mechanism-CVE-2023-29483-.patch new file mode 100644 index 0000000000000000000000000000000000000000..14faad54384e9b69c5bf9a9cacdc408fb32f63e6 --- /dev/null +++ b/Address-DoS-via-the-Tudoor-mechanism-CVE-2023-29483-.patch @@ -0,0 +1,321 @@ +From f66e25b5f549acf66d1fb6ead13eb3cff7d09af3 Mon Sep 17 00:00:00 2001 +From: Bob Halley +Date: Fri, 9 Feb 2024 11:22:52 -0800 +Subject: [PATCH] Address DoS via the Tudoor mechanism (CVE-2023-29483) (#1044) + +--- + dns/asyncquery.py | 45 +++++++++++++------ + dns/nameserver.py | 2 + + dns/query.py | 110 +++++++++++++++++++++++++++++----------------- + 3 files changed, 103 insertions(+), 54 deletions(-) + +diff --git a/dns/asyncquery.py b/dns/asyncquery.py +index 35a355b..94cb241 100644 +--- a/dns/asyncquery.py ++++ b/dns/asyncquery.py +@@ -120,6 +120,8 @@ async def receive_udp( + request_mac: Optional[bytes] = b"", + ignore_trailing: bool = False, + raise_on_truncation: bool = False, ++ ignore_errors: bool = False, ++ query: Optional[dns.message.Message] = None, + ) -> Any: + """Read a DNS message from a UDP socket. + +@@ -133,22 +135,30 @@ async def receive_udp( + """ + + wire = b"" +- while 1: ++ while True: + (wire, from_address) = await sock.recvfrom(65535, _timeout(expiration)) +- if _matches_destination( ++ if not _matches_destination( + sock.family, from_address, destination, ignore_unexpected + ): +- break +- received_time = time.time() +- r = dns.message.from_wire( +- wire, +- keyring=keyring, +- request_mac=request_mac, +- one_rr_per_rrset=one_rr_per_rrset, +- ignore_trailing=ignore_trailing, +- raise_on_truncation=raise_on_truncation, +- ) +- return (r, received_time, from_address) ++ continue ++ received_time = time.time() ++ try: ++ r = dns.message.from_wire( ++ wire, ++ keyring=keyring, ++ request_mac=request_mac, ++ one_rr_per_rrset=one_rr_per_rrset, ++ ignore_trailing=ignore_trailing, ++ raise_on_truncation=raise_on_truncation, ++ ) ++ except Exception: ++ if ignore_errors: ++ continue ++ else: ++ raise ++ if ignore_errors and query is not None and not query.is_response(r): ++ continue ++ return (r, received_time, from_address) + + + async def udp( +@@ -164,6 +174,7 @@ async def udp( + raise_on_truncation: bool = False, + sock: Optional[dns.asyncbackend.DatagramSocket] = None, + backend: Optional[dns.asyncbackend.Backend] = None, ++ ignore_errors: bool = False, + ) -> dns.message.Message: + """Return the response obtained after sending a query via UDP. + +@@ -205,9 +216,13 @@ async def udp( + q.mac, + ignore_trailing, + raise_on_truncation, ++ ignore_errors, ++ q, + ) + r.time = received_time - begin_time +- if not q.is_response(r): ++ # We don't need to check q.is_response() if we are in ignore_errors mode ++ # as receive_udp() will have checked it. ++ if not (ignore_errors or q.is_response(r)): + raise BadResponse + return r + +@@ -225,6 +240,7 @@ async def udp_with_fallback( + udp_sock: Optional[dns.asyncbackend.DatagramSocket] = None, + tcp_sock: Optional[dns.asyncbackend.StreamSocket] = None, + backend: Optional[dns.asyncbackend.Backend] = None, ++ ignore_errors: bool = False, + ) -> Tuple[dns.message.Message, bool]: + """Return the response to the query, trying UDP first and falling back + to TCP if UDP results in a truncated response. +@@ -260,6 +276,7 @@ async def udp_with_fallback( + True, + udp_sock, + backend, ++ ignore_errors, + ) + return (response, False) + except dns.message.Truncated: +diff --git a/dns/nameserver.py b/dns/nameserver.py +index a1fb549..0c494c1 100644 +--- a/dns/nameserver.py ++++ b/dns/nameserver.py +@@ -115,6 +115,7 @@ class Do53Nameserver(AddressAndPortNameserver): + raise_on_truncation=True, + one_rr_per_rrset=one_rr_per_rrset, + ignore_trailing=ignore_trailing, ++ ignore_errors=True, + ) + return response + +@@ -153,6 +154,7 @@ class Do53Nameserver(AddressAndPortNameserver): + backend=backend, + one_rr_per_rrset=one_rr_per_rrset, + ignore_trailing=ignore_trailing, ++ ignore_errors=True, + ) + return response + +diff --git a/dns/query.py b/dns/query.py +index d4bd6b9..bdd251e 100644 +--- a/dns/query.py ++++ b/dns/query.py +@@ -569,6 +569,8 @@ def receive_udp( + request_mac: Optional[bytes] = b"", + ignore_trailing: bool = False, + raise_on_truncation: bool = False, ++ ignore_errors: bool = False, ++ query: Optional[dns.message.Message] = None, + ) -> Any: + """Read a DNS message from a UDP socket. + +@@ -609,28 +611,44 @@ def receive_udp( + ``(dns.message.Message, float, tuple)`` + tuple of the received message, the received time, and the address where + the message arrived from. ++ ++ *ignore_errors*, a ``bool``. If various format errors or response ++ mismatches occur, ignore them and keep listening for a valid response. ++ The default is ``False``. ++ ++ *query*, a ``dns.message.Message`` or ``None``. If not ``None`` and ++ *ignore_errors* is ``True``, check that the received message is a response ++ to this query, and if not keep listening for a valid response. + """ + + wire = b"" + while True: + (wire, from_address) = _udp_recv(sock, 65535, expiration) +- if _matches_destination( ++ if not _matches_destination( + sock.family, from_address, destination, ignore_unexpected + ): +- break +- received_time = time.time() +- r = dns.message.from_wire( +- wire, +- keyring=keyring, +- request_mac=request_mac, +- one_rr_per_rrset=one_rr_per_rrset, +- ignore_trailing=ignore_trailing, +- raise_on_truncation=raise_on_truncation, +- ) +- if destination: +- return (r, received_time) +- else: +- return (r, received_time, from_address) ++ continue ++ received_time = time.time() ++ try: ++ r = dns.message.from_wire( ++ wire, ++ keyring=keyring, ++ request_mac=request_mac, ++ one_rr_per_rrset=one_rr_per_rrset, ++ ignore_trailing=ignore_trailing, ++ raise_on_truncation=raise_on_truncation, ++ ) ++ except Exception: ++ if ignore_errors: ++ continue ++ else: ++ raise ++ if ignore_errors and query is not None and not query.is_response(r): ++ continue ++ if destination: ++ return (r, received_time) ++ else: ++ return (r, received_time, from_address) + + + def udp( +@@ -645,6 +663,7 @@ def udp( + ignore_trailing: bool = False, + raise_on_truncation: bool = False, + sock: Optional[Any] = None, ++ ignore_errors: bool = False, + ) -> dns.message.Message: + """Return the response obtained after sending a query via UDP. + +@@ -681,6 +700,10 @@ def udp( + if a socket is provided, it must be a nonblocking datagram socket, + and the *source* and *source_port* are ignored. + ++ *ignore_errors*, a ``bool``. If various format errors or response ++ mismatches occur, ignore them and keep listening for a valid response. ++ The default is ``False``. ++ + Returns a ``dns.message.Message``. + """ + +@@ -705,9 +728,13 @@ def udp( + q.mac, + ignore_trailing, + raise_on_truncation, ++ ignore_errors, ++ q, + ) + r.time = received_time - begin_time +- if not q.is_response(r): ++ # We don't need to check q.is_response() if we are in ignore_errors mode ++ # as receive_udp() will have checked it. ++ if not (ignore_errors or q.is_response(r)): + raise BadResponse + return r + assert ( +@@ -727,48 +754,50 @@ def udp_with_fallback( + ignore_trailing: bool = False, + udp_sock: Optional[Any] = None, + tcp_sock: Optional[Any] = None, ++ ignore_errors: bool = False, + ) -> Tuple[dns.message.Message, bool]: + """Return the response to the query, trying UDP first and falling back + to TCP if UDP results in a truncated response. + + *q*, a ``dns.message.Message``, the query to send + +- *where*, a ``str`` containing an IPv4 or IPv6 address, where +- to send the message. ++ *where*, a ``str`` containing an IPv4 or IPv6 address, where to send the message. + +- *timeout*, a ``float`` or ``None``, the number of seconds to wait before the +- query times out. If ``None``, the default, wait forever. ++ *timeout*, a ``float`` or ``None``, the number of seconds to wait before the query ++ times out. If ``None``, the default, wait forever. + + *port*, an ``int``, the port send the message to. The default is 53. + +- *source*, a ``str`` containing an IPv4 or IPv6 address, specifying +- the source address. The default is the wildcard address. ++ *source*, a ``str`` containing an IPv4 or IPv6 address, specifying the source ++ address. The default is the wildcard address. + +- *source_port*, an ``int``, the port from which to send the message. +- The default is 0. ++ *source_port*, an ``int``, the port from which to send the message. The default is ++ 0. + +- *ignore_unexpected*, a ``bool``. If ``True``, ignore responses from +- unexpected sources. ++ *ignore_unexpected*, a ``bool``. If ``True``, ignore responses from unexpected ++ sources. + +- *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own +- RRset. ++ *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own RRset. + +- *ignore_trailing*, a ``bool``. If ``True``, ignore trailing +- junk at end of the received message. ++ *ignore_trailing*, a ``bool``. If ``True``, ignore trailing junk at end of the ++ received message. + +- *udp_sock*, a ``socket.socket``, or ``None``, the socket to use for the +- UDP query. If ``None``, the default, a socket is created. Note that +- if a socket is provided, it must be a nonblocking datagram socket, +- and the *source* and *source_port* are ignored for the UDP query. ++ *udp_sock*, a ``socket.socket``, or ``None``, the socket to use for the UDP query. ++ If ``None``, the default, a socket is created. Note that if a socket is provided, ++ it must be a nonblocking datagram socket, and the *source* and *source_port* are ++ ignored for the UDP query. + + *tcp_sock*, a ``socket.socket``, or ``None``, the connected socket to use for the +- TCP query. If ``None``, the default, a socket is created. Note that +- if a socket is provided, it must be a nonblocking connected stream +- socket, and *where*, *source* and *source_port* are ignored for the TCP +- query. ++ TCP query. If ``None``, the default, a socket is created. Note that if a socket is ++ provided, it must be a nonblocking connected stream socket, and *where*, *source* ++ and *source_port* are ignored for the TCP query. ++ ++ *ignore_errors*, a ``bool``. If various format errors or response mismatches occur ++ while listening for UDP, ignore them and keep listening for a valid response. The ++ default is ``False``. + +- Returns a (``dns.message.Message``, tcp) tuple where tcp is ``True`` +- if and only if TCP was used. ++ Returns a (``dns.message.Message``, tcp) tuple where tcp is ``True`` if and only if ++ TCP was used. + """ + try: + response = udp( +@@ -783,6 +812,7 @@ def udp_with_fallback( + ignore_trailing, + True, + udp_sock, ++ ignore_errors, + ) + return (response, False) + except dns.message.Truncated: +-- +2.39.1 + + diff --git a/For-the-Tudoor-fix-we-also-need-the-UDP-nameserver-t.patch b/For-the-Tudoor-fix-we-also-need-the-UDP-nameserver-t.patch new file mode 100644 index 0000000000000000000000000000000000000000..077b9df384ecf5c61db9c2cc7840567431fc3290 --- /dev/null +++ b/For-the-Tudoor-fix-we-also-need-the-UDP-nameserver-t.patch @@ -0,0 +1,35 @@ +From e093299a49967696b1c58b68e4767de5031a3e46 Mon Sep 17 00:00:00 2001 +From: Bob Halley +Date: Fri, 16 Feb 2024 05:47:35 -0800 +Subject: [PATCH] For the Tudoor fix, we also need the UDP nameserver to + ignore_unexpected. + +(cherry picked from commit 5a441b9854425c4e23abb8f91973361fe8401e33) +--- + dns/nameserver.py | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dns/nameserver.py b/dns/nameserver.py +index 030057b..5dbb4e8 100644 +--- a/dns/nameserver.py ++++ b/dns/nameserver.py +@@ -116,6 +116,7 @@ class Do53Nameserver(AddressAndPortNameserver): + one_rr_per_rrset=one_rr_per_rrset, + ignore_trailing=ignore_trailing, + ignore_errors=True, ++ ignore_unexpected=True, + ) + return response + +@@ -155,6 +156,7 @@ class Do53Nameserver(AddressAndPortNameserver): + one_rr_per_rrset=one_rr_per_rrset, + ignore_trailing=ignore_trailing, + ignore_errors=True, ++ ignore_unexpected=True, + ) + return response + +-- +2.39.1 + + diff --git a/python-dns.spec b/python-dns.spec index a5c76566c7650c92e266841fc975b0f65efa9e2c..b90f2361791d882bd24bd8cc9b786bb24e34ccdb 100644 --- a/python-dns.spec +++ b/python-dns.spec @@ -14,11 +14,14 @@ messages, names, and records. Name: python-dns Summary: %{sum} Version: 2.4.2 -Release: 1 +Release: 2 License: ISC and MIT URL: http://www.dnspython.org/ Source0: https://github.com/rthalley/dnspython/archive/v%{version}/dnspython-%{version}.tar.gz +Patch0001: Address-DoS-via-the-Tudoor-mechanism-CVE-2023-29483-.patch +Patch0002: For-the-Tudoor-fix-we-also-need-the-UDP-nameserver-t.patch + BuildArch: noarch BuildRequires: python3-devel python3-setuptools python3-cryptography @@ -60,6 +63,9 @@ pytest %doc examples %changelog +* Thu Apr 18 2024 wangguochun - 2.4.2-2 +- fix CVE-2023-29483 + * Tue Dec 26 2023 gaihuiying - 2.4.2-1 - update to 2.4.2