pkgsrc-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[pkgsrc/trunk]: pkgsrc lang/python27: backport vulnerability fixes from Gentoo
details: https://anonhg.NetBSD.org/pkgsrc/rev/5361fbc49071
branches: trunk
changeset: 439530:5361fbc49071
user: mgorny <mgorny%pkgsrc.org@localhost>
date: Sun Sep 20 11:06:23 2020 +0000
description:
lang/python27: backport vulnerability fixes from Gentoo
Backport 3 vulnerability fixes from Python 3.6 using rebased patches
from Gentoo. These are:
bpo-39017 (CVE-2019-20907): infinite loop in tarfile.py
bpo-39503 (CVE-2020-8492): ReDoS on AbstractBasicAuthHandler
bpo-39603 (no CVE): header injection via HTTP method
diffstat:
doc/CHANGES-2020 | 3 +-
lang/python27/Makefile | 4 +-
lang/python27/distinfo | 7 +-
lang/python27/patches/patch-Lib_httplib.py | 42 ++++++++
lang/python27/patches/patch-Lib_tarfile.py | 13 ++
lang/python27/patches/patch-Lib_test_test__httplib.py | 31 ++++++
lang/python27/patches/patch-Lib_test_test__urllib2.py | 97 +++++++++++++++++++
lang/python27/patches/patch-Lib_urllib2.py | 86 ++++++++++++++++
8 files changed, 279 insertions(+), 4 deletions(-)
diffs (truncated from 343 to 300 lines):
diff -r 8608492e1fde -r 5361fbc49071 doc/CHANGES-2020
--- a/doc/CHANGES-2020 Sun Sep 20 11:05:31 2020 +0000
+++ b/doc/CHANGES-2020 Sun Sep 20 11:06:23 2020 +0000
@@ -1,4 +1,4 @@
-$NetBSD: CHANGES-2020,v 1.5384 2020/09/20 09:55:27 mef Exp $
+$NetBSD: CHANGES-2020,v 1.5385 2020/09/20 11:06:23 mgorny Exp $
Changes to the packages collection and infrastructure in 2020:
@@ -8060,3 +8060,4 @@
Updated textproc/R-DT to 0.15 [mef 2020-09-20]
Updated lang/llvm to 10.0.1nb1 [he 2020-09-20]
Updated textproc/R-jsonlite to 1.7.1 [mef 2020-09-20]
+ Updated lang/python27 to 2.7.18nb3 [mgorny 2020-09-20]
diff -r 8608492e1fde -r 5361fbc49071 lang/python27/Makefile
--- a/lang/python27/Makefile Sun Sep 20 11:05:31 2020 +0000
+++ b/lang/python27/Makefile Sun Sep 20 11:06:23 2020 +0000
@@ -1,9 +1,9 @@
-# $NetBSD: Makefile,v 1.89 2020/09/01 09:26:54 schmonz Exp $
+# $NetBSD: Makefile,v 1.90 2020/09/20 11:06:23 mgorny Exp $
.include "dist.mk"
PKGNAME= python27-${PY_DISTVERSION}
-PKGREVISION= 2
+PKGREVISION= 3
CATEGORIES= lang python
MAINTAINER= pkgsrc-users%NetBSD.org@localhost
diff -r 8608492e1fde -r 5361fbc49071 lang/python27/distinfo
--- a/lang/python27/distinfo Sun Sep 20 11:05:31 2020 +0000
+++ b/lang/python27/distinfo Sun Sep 20 11:06:23 2020 +0000
@@ -1,4 +1,4 @@
-$NetBSD: distinfo,v 1.78 2020/09/01 09:26:54 schmonz Exp $
+$NetBSD: distinfo,v 1.79 2020/09/20 11:06:23 mgorny Exp $
SHA1 (Python-2.7.18.tar.xz) = 678d4cf483a1c92efd347ee8e1e79326dc82810b
RMD160 (Python-2.7.18.tar.xz) = 40a514bb05c9e631454ea8466e28f5bb229428ad
@@ -13,10 +13,15 @@
SHA1 (patch-Lib_distutils_command_install__egg__info.py) = ec7f9e0cd04489b1f6497c44d75bff6864ad1047
SHA1 (patch-Lib_distutils_unixccompiler.py) = db16c9aca2f29730945f28247b88b18828739bbb
SHA1 (patch-Lib_distutils_util.py) = 5bcfad96f8e490351160f1a7c1f4ece7706a33fa
+SHA1 (patch-Lib_httplib.py) = 685891e4ea58062036c87e69e8a0911dd377e64f
SHA1 (patch-Lib_lib2to3_pgen2_driver.py) = 5d6dab14197f27363394ff1aeee22a8ced8026d2
SHA1 (patch-Lib_multiprocessing_process.py) = 15699bd8ec822bf54a0631102e00e0a34f882803
SHA1 (patch-Lib_plistlib.py) = 96ae702995d434e2d7ec0ac62e37427a90b61d13
SHA1 (patch-Lib_sysconfig.py) = 8a7a0e5cbfec279a05945dffafea1b1131a76f0e
+SHA1 (patch-Lib_tarfile.py) = 105bd378ccd1574ef765cf417269759363148876
+SHA1 (patch-Lib_test_test__httplib.py) = 867d48f677fce23b05c512c0839e704d89e70f14
+SHA1 (patch-Lib_test_test__urllib2.py) = 651d99068f5d25fa9dc1e9be7e923db64920b378
+SHA1 (patch-Lib_urllib2.py) = 8c3b3b1796b57fe544f118313da157d38263b0e5
SHA1 (patch-Makefile.pre.in) = ceaf34237588b527478ce1f9163c9168382fa201
SHA1 (patch-Modules___multiprocessing_multiprocessing.h) = 7ca8fe22ba4bdcde6d39dd50fe2e86c25994c146
SHA1 (patch-Modules___multiprocessing_semaphore.c) = 03b9c33ef38da383d5f7c2c84c17fe38cdd2911e
diff -r 8608492e1fde -r 5361fbc49071 lang/python27/patches/patch-Lib_httplib.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lang/python27/patches/patch-Lib_httplib.py Sun Sep 20 11:06:23 2020 +0000
@@ -0,0 +1,42 @@
+$NetBSD: patch-Lib_httplib.py,v 1.1 2020/09/20 11:06:23 mgorny Exp $
+
+--- Lib/httplib.py.orig 2020-04-19 21:13:39.000000000 +0000
++++ Lib/httplib.py
+@@ -257,6 +257,10 @@ _contains_disallowed_url_pchar_re = re.c
+ # _is_allowed_url_pchars_re = re.compile(r"^[/!$&'()*+,;=:@%a-zA-Z0-9._~-]+$")
+ # We are more lenient for assumed real world compatibility purposes.
+
++# These characters are not allowed within HTTP method names
++# to prevent http header injection.
++_contains_disallowed_method_pchar_re = re.compile('[\x00-\x1f]')
++
+ # We always set the Content-Length header for these methods because some
+ # servers will otherwise respond with a 411
+ _METHODS_EXPECTING_BODY = {'PATCH', 'POST', 'PUT'}
+@@ -935,6 +939,8 @@ class HTTPConnection:
+ else:
+ raise CannotSendRequest()
+
++ self._validate_method(method)
++
+ # Save the method for use later in the response phase
+ self._method = method
+
+@@ -1020,6 +1026,17 @@ class HTTPConnection:
+ # On Python 2, request is already encoded (default)
+ return request
+
++ def _validate_method(self, method):
++ """Validate a method name for putrequest."""
++ # prevent http header injection
++ match = _contains_disallowed_method_pchar_re.search(method)
++ if match:
++ msg = (
++ "method can't contain control characters. {method!r} "
++ "(found at least {matched!r})"
++ ).format(matched=match.group(), method=method)
++ raise ValueError(msg)
++
+ def _validate_path(self, url):
+ """Validate a url for putrequest."""
+ # Prevent CVE-2019-9740.
diff -r 8608492e1fde -r 5361fbc49071 lang/python27/patches/patch-Lib_tarfile.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lang/python27/patches/patch-Lib_tarfile.py Sun Sep 20 11:06:23 2020 +0000
@@ -0,0 +1,13 @@
+$NetBSD: patch-Lib_tarfile.py,v 1.1 2020/09/20 11:06:23 mgorny Exp $
+
+--- Lib/tarfile.py.orig 2020-04-19 21:13:39.000000000 +0000
++++ Lib/tarfile.py
+@@ -1400,6 +1400,8 @@ class TarInfo(object):
+
+ length, keyword = match.groups()
+ length = int(length)
++ if length == 0:
++ raise InvalidHeaderError("invalid header")
+ value = buf[match.end(2) + 1:match.start(1) + length - 1]
+
+ keyword = keyword.decode("utf8")
diff -r 8608492e1fde -r 5361fbc49071 lang/python27/patches/patch-Lib_test_test__httplib.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lang/python27/patches/patch-Lib_test_test__httplib.py Sun Sep 20 11:06:23 2020 +0000
@@ -0,0 +1,31 @@
+$NetBSD: patch-Lib_test_test__httplib.py,v 1.1 2020/09/20 11:06:23 mgorny Exp $
+
+--- Lib/test/test_httplib.py.orig 2020-04-19 21:13:39.000000000 +0000
++++ Lib/test/test_httplib.py
+@@ -384,6 +384,26 @@ class HeaderTests(TestCase):
+ with self.assertRaisesRegexp(ValueError, 'Invalid header'):
+ conn.putheader(name, value)
+
++ def test_invalid_method_names(self):
++ methods = (
++ 'GET\r',
++ 'POST\n',
++ 'PUT\n\r',
++ 'POST\nValue',
++ 'POST\nHOST:abc',
++ 'GET\nrHost:abc\n',
++ 'POST\rRemainder:\r',
++ 'GET\rHOST:\n',
++ '\nPUT'
++ )
++
++ for method in methods:
++ with self.assertRaisesRegexp(
++ ValueError, "method can't contain control characters"):
++ conn = httplib.HTTPConnection('example.com')
++ conn.sock = FakeSocket(None)
++ conn.request(method=method, url="/")
++
+
+ class BasicTest(TestCase):
+ def test_status_lines(self):
diff -r 8608492e1fde -r 5361fbc49071 lang/python27/patches/patch-Lib_test_test__urllib2.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lang/python27/patches/patch-Lib_test_test__urllib2.py Sun Sep 20 11:06:23 2020 +0000
@@ -0,0 +1,97 @@
+$NetBSD: patch-Lib_test_test__urllib2.py,v 1.1 2020/09/20 11:06:23 mgorny Exp $
+
+--- Lib/test/test_urllib2.py.orig 2020-04-19 21:13:39.000000000 +0000
++++ Lib/test/test_urllib2.py
+@@ -1128,42 +1128,67 @@ class HandlerTests(unittest.TestCase):
+ self.assertEqual(req.get_host(), "proxy.example.com:3128")
+ self.assertEqual(req.get_header("Proxy-authorization"),"FooBar")
+
+- def test_basic_auth(self, quote_char='"'):
++ def check_basic_auth(self, headers, realm):
+ opener = OpenerDirector()
+ password_manager = MockPasswordManager()
+ auth_handler = urllib2.HTTPBasicAuthHandler(password_manager)
+- realm = "ACME Widget Store"
+- http_handler = MockHTTPHandler(
+- 401, 'WWW-Authenticate: Basic realm=%s%s%s\r\n\r\n' %
+- (quote_char, realm, quote_char) )
++ body = '\r\n'.join(headers) + '\r\n\r\n'
++ http_handler = MockHTTPHandler(401, body)
+ opener.add_handler(auth_handler)
+ opener.add_handler(http_handler)
+ self._test_basic_auth(opener, auth_handler, "Authorization",
+ realm, http_handler, password_manager,
+ "http://acme.example.com/protected",
+- "http://acme.example.com/protected"
+- )
++ "http://acme.example.com/protected")
+
+- def test_basic_auth_with_single_quoted_realm(self):
+- self.test_basic_auth(quote_char="'")
++ def test_basic_auth(self):
++ realm = "realm2%example.com@localhost"
++ realm2 = "realm2%example.com@localhost"
++ basic = 'Basic realm="{realm}"'.format(realm=realm)
++ basic2 = 'Basic realm="{realm2}"'.format(realm2=realm2)
++ other_no_realm = 'Otherscheme xxx'
++ digest = ('Digest realm="{realm2}", '
++ 'qop="auth, auth-int", '
++ 'nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", '
++ 'opaque="5ccc069c403ebaf9f0171e9517f40e41"'
++ .format(realm2=realm2))
++ for realm_str in (
++ # test "quote" and 'quote'
++ 'Basic realm="{realm}"'.format(realm=realm),
++ "Basic realm='{realm}'".format(realm=realm),
+
+- def test_basic_auth_with_unquoted_realm(self):
+- opener = OpenerDirector()
+- password_manager = MockPasswordManager()
+- auth_handler = urllib2.HTTPBasicAuthHandler(password_manager)
+- realm = "ACME Widget Store"
+- http_handler = MockHTTPHandler(
+- 401, 'WWW-Authenticate: Basic realm=%s\r\n\r\n' % realm)
+- opener.add_handler(auth_handler)
+- opener.add_handler(http_handler)
+- msg = "Basic Auth Realm was unquoted"
+- with test_support.check_warnings((msg, UserWarning)):
+- self._test_basic_auth(opener, auth_handler, "Authorization",
+- realm, http_handler, password_manager,
+- "http://acme.example.com/protected",
+- "http://acme.example.com/protected"
+- )
++ # charset is ignored
++ 'Basic realm="{realm}", charset="UTF-8"'.format(realm=realm),
++
++ # Multiple challenges per header
++ ', '.join((basic, basic2)),
++ ', '.join((basic, other_no_realm)),
++ ', '.join((other_no_realm, basic)),
++ ', '.join((basic, digest)),
++ ', '.join((digest, basic)),
++ ):
++ headers = ['WWW-Authenticate: {realm_str}'
++ .format(realm_str=realm_str)]
++ self.check_basic_auth(headers, realm)
++
++ # no quote: expect a warning
++ with test_support.check_warnings(("Basic Auth Realm was unquoted",
++ UserWarning)):
++ headers = ['WWW-Authenticate: Basic realm={realm}'
++ .format(realm=realm)]
++ self.check_basic_auth(headers, realm)
+
++ # Multiple headers: one challenge per header.
++ # Use the first Basic realm.
++ for challenges in (
++ [basic, basic2],
++ [basic, digest],
++ [digest, basic],
++ ):
++ headers = ['WWW-Authenticate: {challenge}'
++ .format(challenge=challenge)
++ for challenge in challenges]
++ self.check_basic_auth(headers, realm)
+
+ def test_proxy_basic_auth(self):
+ opener = OpenerDirector()
diff -r 8608492e1fde -r 5361fbc49071 lang/python27/patches/patch-Lib_urllib2.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lang/python27/patches/patch-Lib_urllib2.py Sun Sep 20 11:06:23 2020 +0000
@@ -0,0 +1,86 @@
+$NetBSD: patch-Lib_urllib2.py,v 1.1 2020/09/20 11:06:23 mgorny Exp $
+
+--- Lib/urllib2.py.orig 2020-04-19 21:13:39.000000000 +0000
++++ Lib/urllib2.py
+@@ -856,8 +856,15 @@ class AbstractBasicAuthHandler:
+
+ # allow for double- and single-quoted realm values
+ # (single quotes are a violation of the RFC, but appear in the wild)
+- rx = re.compile('(?:.*,)*[ \t]*([^ \t]+)[ \t]+'
+- 'realm=(["\']?)([^"\']*)\\2', re.I)
++ rx = re.compile('(?:^|,)' # start of the string or ','
++ '[ \t]*' # optional whitespaces
++ '([^ \t]+)' # scheme like "Basic"
++ '[ \t]+' # mandatory whitespaces
++ # realm=xxx
++ # realm='xxx'
++ # realm="xxx"
++ 'realm=(["\']?)([^"\']*)\\2',
++ re.I)
+
+ # XXX could pre-emptively send auth info already accepted (RFC 2617,
+ # end of section 2, and section 1.2 immediately after "credentials"
+@@ -869,23 +876,52 @@ class AbstractBasicAuthHandler:
+ self.passwd = password_mgr
+ self.add_password = self.passwd.add_password
+
++ def _parse_realm(self, header):
++ # parse WWW-Authenticate header: accept multiple challenges per header
++ found_challenge = False
++ for mo in AbstractBasicAuthHandler.rx.finditer(header):
++ scheme, quote, realm = mo.groups()
++ if quote not in ['"', "'"]:
++ warnings.warn("Basic Auth Realm was unquoted",
++ UserWarning, 3)
++
++ yield (scheme, realm)
++
++ found_challenge = True
++
++ if not found_challenge:
++ if header:
++ scheme = header.split()[0]
++ else:
Home |
Main Index |
Thread Index |
Old Index