python rebuild python3 ver.bum

This commit is contained in:
Rmys
2021-08-29 11:05:39 +03:00
parent 7824ed40cf
commit ab60302ced
10 changed files with 871 additions and 8 deletions
@@ -0,0 +1,133 @@
From 117bd40b3844733e96a8f5ccc9460900698cffbe Mon Sep 17 00:00:00 2001
From: "Miss Islington (bot)"
<31488909+miss-islington@users.noreply.github.com>
Date: Tue, 16 Mar 2021 14:08:30 -0700
Subject: [PATCH 26/30] [3.6] bpo-43285 Make ftplib not trust the PASV
response. (GH-24838) (GH-24881) (GH-24882)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The IPv4 address value returned from the server in response to the PASV command
should not be trusted. This prevents a malicious FTP server from using the
response to probe IPv4 address and port combinations on the client network.
Instead of using the returned address, we use the IP address we're
already connected to. This is the strategy other ftp clients adopted,
and matches the only strategy available for the modern IPv6 EPSV command
where the server response must return a port number and nothing else.
For the rare user who _wants_ this ugly behavior, set a `trust_server_pasv_ipv4_address`
attribute on your `ftplib.FTP` instance to True..
(cherry picked from commit 0ab152c6b5d95caa2dc1a30fa96e10258b5f188e)
Co-authored-by: Gregory P. Smith <greg@krypto.org>
(cherry picked from commit 664d1d16274b47eea6ec92572e1ebf3939a6fa0c)
Rebased for Python 2.7 by Michał Górny <mgorny@gentoo.org>
---
Lib/ftplib.py | 9 +++++-
Lib/test/test_ftplib.py | 29 +++++++++++++++++--
.../2021-03-13-03-48-14.bpo-43285.g-Hah3.rst | 8 +++++
3 files changed, 43 insertions(+), 3 deletions(-)
create mode 100644 Misc/NEWS.d/next/Security/2021-03-13-03-48-14.bpo-43285.g-Hah3.rst
diff --git a/Lib/ftplib.py b/Lib/ftplib.py
index 6644554792..0550f0ab9f 100644
--- a/Lib/ftplib.py
+++ b/Lib/ftplib.py
@@ -108,6 +108,8 @@ class FTP:
file = None
welcome = None
passiveserver = 1
+ # Disables https://bugs.python.org/issue43285 security if set to True.
+ trust_server_pasv_ipv4_address = False
# Initialization method (called by class instantiation).
# Initialize host to localhost, port to standard ftp port
@@ -310,8 +312,13 @@ class FTP:
return sock
def makepasv(self):
+ """Internal: Does the PASV or EPSV handshake -> (address, port)"""
if self.af == socket.AF_INET:
- host, port = parse227(self.sendcmd('PASV'))
+ untrusted_host, port = parse227(self.sendcmd('PASV'))
+ if self.trust_server_pasv_ipv4_address:
+ host = untrusted_host
+ else:
+ host = self.sock.getpeername()[0]
else:
host, port = parse229(self.sendcmd('EPSV'), self.sock.getpeername())
return host, port
diff --git a/Lib/test/test_ftplib.py b/Lib/test/test_ftplib.py
index 8a3eb067a4..f3217d686d 100644
--- a/Lib/test/test_ftplib.py
+++ b/Lib/test/test_ftplib.py
@@ -67,6 +67,10 @@ class DummyFTPHandler(asynchat.async_chat):
self.rest = None
self.next_retr_data = RETR_DATA
self.push('220 welcome')
+ # We use this as the string IPv4 address to direct the client
+ # to in response to a PASV command. To test security behavior.
+ # https://bugs.python.org/issue43285/.
+ self.fake_pasv_server_ip = '252.253.254.255'
def collect_incoming_data(self, data):
self.in_buffer.append(data)
@@ -109,8 +113,9 @@ class DummyFTPHandler(asynchat.async_chat):
sock.bind((self.socket.getsockname()[0], 0))
sock.listen(5)
sock.settimeout(10)
- ip, port = sock.getsockname()[:2]
- ip = ip.replace('.', ',')
+ port = sock.getsockname()[1]
+ ip = self.fake_pasv_server_ip
+ ip = ip.replace('.', ','); p1 = port // 256; p2 = port % 256
p1, p2 = divmod(port, 256)
self.push('227 entering passive mode (%s,%d,%d)' %(ip, p1, p2))
conn, addr = sock.accept()
@@ -577,6 +582,26 @@ class TestFTPClass(TestCase):
# IPv4 is in use, just make sure send_epsv has not been used
self.assertEqual(self.server.handler_instance.last_received_cmd, 'pasv')
+ def test_makepasv_issue43285_security_disabled(self):
+ """Test the opt-in to the old vulnerable behavior."""
+ self.client.trust_server_pasv_ipv4_address = True
+ bad_host, port = self.client.makepasv()
+ self.assertEqual(
+ bad_host, self.server.handler_instance.fake_pasv_server_ip)
+ # Opening and closing a connection keeps the dummy server happy
+ # instead of timing out on accept.
+ socket.create_connection((self.client.sock.getpeername()[0], port),
+ timeout=TIMEOUT).close()
+
+ def test_makepasv_issue43285_security_enabled_default(self):
+ self.assertFalse(self.client.trust_server_pasv_ipv4_address)
+ trusted_host, port = self.client.makepasv()
+ self.assertNotEqual(
+ trusted_host, self.server.handler_instance.fake_pasv_server_ip)
+ # Opening and closing a connection keeps the dummy server happy
+ # instead of timing out on accept.
+ socket.create_connection((trusted_host, port), timeout=TIMEOUT).close()
+
def test_line_too_long(self):
self.assertRaises(ftplib.Error, self.client.sendcmd,
'x' * self.client.maxline * 2)
diff --git a/Misc/NEWS.d/next/Security/2021-03-13-03-48-14.bpo-43285.g-Hah3.rst b/Misc/NEWS.d/next/Security/2021-03-13-03-48-14.bpo-43285.g-Hah3.rst
new file mode 100644
index 0000000000..8312b7e885
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2021-03-13-03-48-14.bpo-43285.g-Hah3.rst
@@ -0,0 +1,8 @@
+:mod:`ftplib` no longer trusts the IP address value returned from the server
+in response to the PASV command by default. This prevents a malicious FTP
+server from using the response to probe IPv4 address and port combinations
+on the client network.
+
+Code that requires the former vulnerable behavior may set a
+``trust_server_pasv_ipv4_address`` attribute on their
+:class:`ftplib.FTP` instances to ``True`` to re-enable it.
--
2.32.0
@@ -0,0 +1,39 @@
From 93742fbb8de75c036b552294fc2bd73eafa47592 Mon Sep 17 00:00:00 2001
From: "Miss Islington (bot)"
<31488909+miss-islington@users.noreply.github.com>
Date: Wed, 7 Apr 2021 04:45:05 -0700
Subject: [PATCH 27/30] bpo-43075: Fix ReDoS in urllib AbstractBasicAuthHandler
(GH-24391)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Fix Regular Expression Denial of Service (ReDoS) vulnerability in
urllib.request.AbstractBasicAuthHandler. The ReDoS-vulnerable regex
has quadratic worst-case complexity and it allows cause a denial of
service when identifying crafted invalid RFCs. This ReDoS issue is on
the client side and needs remote attackers to control the HTTP server.
(cherry picked from commit 7215d1ae25525c92b026166f9d5cac85fb1defe1)
Co-authored-by: Yeting Li <liyt@ios.ac.cn>
(backported to Python 2.7 by Michał Górny)
---
Lib/urllib2.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Lib/urllib2.py b/Lib/urllib2.py
index b2d1fad6f2..c92fb6afb8 100644
--- a/Lib/urllib2.py
+++ b/Lib/urllib2.py
@@ -858,7 +858,7 @@ class AbstractBasicAuthHandler:
# (single quotes are a violation of the RFC, but appear in the wild)
rx = re.compile('(?:^|,)' # start of the string or ','
'[ \t]*' # optional whitespaces
- '([^ \t]+)' # scheme like "Basic"
+ '([^ \t,]+)' # scheme like "Basic"
'[ \t]+' # mandatory whitespaces
# realm=xxx
# realm='xxx'
--
2.32.0
@@ -0,0 +1,150 @@
From 429f40abd4dfc8d8b795de68ff9e268a43b12c25 Mon Sep 17 00:00:00 2001
From: "Miss Islington (bot)"
<31488909+miss-islington@users.noreply.github.com>
Date: Thu, 29 Apr 2021 10:57:31 -0700
Subject: [PATCH 28/30] [3.9] bpo-43882 - urllib.parse should sanitize urls
containing ASCII newline and tabs. (GH-25595) (GH-25725)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* bpo-43882 - urllib.parse should sanitize urls containing ASCII newline and tabs. (GH-25595)
Co-authored-by: Gregory P. Smith <greg@krypto.org>
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
(cherry picked from commit 76cd81d60310d65d01f9d7b48a8985d8ab89c8b4)
Co-authored-by: Senthil Kumaran <skumaran@gatech.edu>
(backported to Python 2.7 by Michał Górny)
---
Doc/library/urlparse.rst | 13 +++++++++
Lib/test/test_urlparse.py | 29 +++++++++++++++++++
Lib/urlparse.py | 7 +++++
.../2021-04-25-07-46-37.bpo-43882.Jpwx85.rst | 6 ++++
4 files changed, 55 insertions(+)
create mode 100644 Misc/NEWS.d/next/Security/2021-04-25-07-46-37.bpo-43882.Jpwx85.rst
diff --git a/Doc/library/urlparse.rst b/Doc/library/urlparse.rst
index 2f8e4c5a44..0546dcae8c 100644
--- a/Doc/library/urlparse.rst
+++ b/Doc/library/urlparse.rst
@@ -267,6 +267,9 @@ The :mod:`urlparse` module defines the following functions:
decomposed before parsing, or is not a Unicode string, no error will be
raised.
+ Following the `WHATWG spec`_ that updates RFC 3986, ASCII newline
+ ``\n``, ``\r`` and tab ``\t`` characters are stripped from the URL.
+
.. versionadded:: 2.2
.. versionchanged:: 2.5
@@ -276,6 +279,10 @@ The :mod:`urlparse` module defines the following functions:
Characters that affect netloc parsing under NFKC normalization will
now raise :exc:`ValueError`.
+ .. versionchanged:: 2.7.18_p9 (Gentoo)
+ ASCII newline and tab characters are stripped from the URL.
+
+.. _WHATWG spec: https://url.spec.whatwg.org/#concept-basic-url-parser
.. function:: urlunsplit(parts)
@@ -327,6 +334,10 @@ The :mod:`urlparse` module defines the following functions:
.. seealso::
+ `WHATWG`_ - URL Living standard
+ Working Group for the URL Standard that defines URLs, domains, IP addresses, the
+ application/x-www-form-urlencoded format, and their API.
+
:rfc:`3986` - Uniform Resource Identifiers
This is the current standard (STD66). Any changes to urlparse module
should conform to this. Certain deviations could be observed, which are
@@ -351,6 +362,8 @@ The :mod:`urlparse` module defines the following functions:
:rfc:`1738` - Uniform Resource Locators (URL)
This specifies the formal syntax and semantics of absolute URLs.
+.. _WHATWG: https://url.spec.whatwg.org/
+
.. _urlparse-result-object:
diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py
index 0b2107339a..9912fe2888 100644
--- a/Lib/test/test_urlparse.py
+++ b/Lib/test/test_urlparse.py
@@ -543,6 +543,35 @@ class UrlParseTestCase(unittest.TestCase):
self.assertEqual(p1.params, 'phone-context=+1-914-555')
+ def test_urlsplit_remove_unsafe_bytes(self):
+ # Remove ASCII tabs and newlines from input
+ url = "http://www.python.org/java\nscript:\talert('msg\r\n')/#frag"
+ p = urlparse.urlsplit(url)
+ self.assertEqual(p.scheme, "http")
+ self.assertEqual(p.netloc, "www.python.org")
+ self.assertEqual(p.path, "/javascript:alert('msg')/")
+ self.assertEqual(p.query, "")
+ self.assertEqual(p.fragment, "frag")
+ self.assertEqual(p.username, None)
+ self.assertEqual(p.password, None)
+ self.assertEqual(p.hostname, "www.python.org")
+ self.assertEqual(p.port, None)
+ self.assertEqual(p.geturl(), "http://www.python.org/javascript:alert('msg')/#frag")
+
+ # Remove ASCII tabs and newlines from input as bytes.
+ url = b"http://www.python.org/java\nscript:\talert('msg\r\n')/#frag"
+ p = urlparse.urlsplit(url)
+ self.assertEqual(p.scheme, b"http")
+ self.assertEqual(p.netloc, b"www.python.org")
+ self.assertEqual(p.path, b"/javascript:alert('msg')/")
+ self.assertEqual(p.query, b"")
+ self.assertEqual(p.fragment, b"frag")
+ self.assertEqual(p.username, None)
+ self.assertEqual(p.password, None)
+ self.assertEqual(p.hostname, b"www.python.org")
+ self.assertEqual(p.port, None)
+ self.assertEqual(p.geturl(), b"http://www.python.org/javascript:alert('msg')/#frag")
+
def test_attributes_bad_port(self):
"""Check handling of non-integer ports."""
p = urlparse.urlsplit("http://www.example.net:foo")
diff --git a/Lib/urlparse.py b/Lib/urlparse.py
index 6c32727fce..4f24cc12c6 100644
--- a/Lib/urlparse.py
+++ b/Lib/urlparse.py
@@ -62,6 +62,9 @@ scheme_chars = ('abcdefghijklmnopqrstuvwxyz'
'0123456789'
'+-.')
+# Unsafe bytes to be removed per WHATWG spec
+_UNSAFE_URL_BYTES_TO_REMOVE = ['\t', '\r', '\n']
+
MAX_CACHE_SIZE = 20
_parse_cache = {}
@@ -198,6 +201,10 @@ def urlsplit(url, scheme='', allow_fragments=True):
if len(_parse_cache) >= MAX_CACHE_SIZE: # avoid runaway growth
clear_cache()
netloc = query = fragment = ''
+
+ for b in _UNSAFE_URL_BYTES_TO_REMOVE:
+ url = url.replace(b, "")
+
i = url.find(':')
if i > 0:
if url[:i] == 'http': # optimize the common case
diff --git a/Misc/NEWS.d/next/Security/2021-04-25-07-46-37.bpo-43882.Jpwx85.rst b/Misc/NEWS.d/next/Security/2021-04-25-07-46-37.bpo-43882.Jpwx85.rst
new file mode 100644
index 0000000000..a326d079df
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2021-04-25-07-46-37.bpo-43882.Jpwx85.rst
@@ -0,0 +1,6 @@
+The presence of newline or tab characters in parts of a URL could allow
+some forms of attacks.
+
+Following the controlling specification for URLs defined by WHATWG
+:func:`urllib.parse` now removes ASCII newlines and tabs from URLs,
+preventing such attacks.
--
2.32.0
@@ -0,0 +1,83 @@
From e1a42c83c6cae70dd42c6ecf1261049edce5ce63 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= <mgorny@gentoo.org>
Date: Sat, 19 Jun 2021 20:46:09 +0200
Subject: [PATCH 30/30] Backport bpo-44022: Fix http client infinite line
reading (DoS) after a HTTP 100 Continue (GH-25916)
Backport the fix from the following commit:
commit 47895e31b6f626bc6ce47d175fe9d43c1098909d
Author: Gen Xu <xgbarry@gmail.com>
Date: 2021-05-06 00:42:41 +0200
bpo-44022: Fix http client infinite line reading (DoS) after a HTTP 100 Continue (GH-25916)
Fixes http.client potential denial of service where it could get stuck reading lines from a malicious server after a 100 Continue response.
Co-authored-by: Gregory P. Smith <greg@krypto.org>
Instead of reusing the header reading code, I have just added explicit
counter to avoid having to refactor the old code.
Plus the improved test from:
commit e60ab843cbb016fb6ff8b4f418641ac05a9b2fcc
Author: Gregory P. Smith <greg@krypto.org>
Date: 2021-06-03 05:43:38 +0200
bpo-44022: Improve the regression test. (GH-26503)
It wasn't actually detecting the regression due to the
assertion being too lenient.
---
Lib/httplib.py | 5 ++++-
Lib/test/test_httplib.py | 13 +++++++++++++
2 files changed, 17 insertions(+), 1 deletion(-)
diff --git a/Lib/httplib.py b/Lib/httplib.py
index 81a08d5d71..ebfa59ff93 100644
--- a/Lib/httplib.py
+++ b/Lib/httplib.py
@@ -453,11 +453,14 @@ class HTTPResponse:
if status != CONTINUE:
break
# skip the header from the 100 response
+ header_count = 0
while True:
skip = self.fp.readline(_MAXLINE + 1)
if len(skip) > _MAXLINE:
raise LineTooLong("header line")
- skip = skip.strip()
+ header_count += 1
+ if header_count > _MAXHEADERS:
+ raise HTTPException("got more than %d headers" % _MAXHEADERS)
if not skip:
break
if self.debuglevel > 0:
diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py
index e20a0986dc..5f6b1d0b20 100644
--- a/Lib/test/test_httplib.py
+++ b/Lib/test/test_httplib.py
@@ -675,6 +675,19 @@ class BasicTest(TestCase):
resp = httplib.HTTPResponse(FakeSocket(body))
self.assertRaises(httplib.LineTooLong, resp.begin)
+ def test_overflowing_header_limit_after_100(self):
+ body = (
+ 'HTTP/1.1 100 OK\r\n'
+ 'r\n' * 32768
+ )
+ resp = httplib.HTTPResponse(FakeSocket(body))
+ with self.assertRaises(httplib.HTTPException) as cm:
+ resp.begin()
+ # We must assert more because other reasonable errors that we
+ # do not want can also be HTTPException derived.
+ self.assertIn('got more than ', str(cm.exception))
+ self.assertIn('headers', str(cm.exception))
+
def test_overflowing_chunked_line(self):
body = (
'HTTP/1.1 200 OK\r\n'
--
2.32.0