From ddb0af40614e9398d4a9f48cfeb4f0946a200b14 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Mon, 20 Jul 2020 06:02:38 +0800 Subject: [PATCH] python3: Backport security fixes This backports fixes for security issues, including: * CVE-2020-14422: Hash collisions in IPv4Interface and IPv6Interface * CVE-2019-20907: Infinite loop in the tarfile module Signed-off-by: Jeffery To --- lang/python/python3/Makefile | 2 +- ...-and-IPv6Interface-GH-21033-GH-21231.patch | 74 ++++++++++++ ...n-SSLContext.load_dh_params-GH-21389.patch | 40 +++++++ ...-invalid-NEWOBJ_EX-GH-21458-GH-21461.patch | 111 ++++++++++++++++++ ...the-tarfile-module-GH-21454-GH-21484.patch | 59 ++++++++++ ...on-in-http-methods-GH-18485-GH-21538.patch | 99 ++++++++++++++++ 6 files changed, 384 insertions(+), 1 deletion(-) create mode 100644 lang/python/python3/patches/027-bpo-41004-Resolve-hash-collisions-for-IPv4Interface-and-IPv6Interface-GH-21033-GH-21231.patch create mode 100644 lang/python/python3/patches/028-closes-bpo-41235-Fix-the-error-handling-in-SSLContext.load_dh_params-GH-21389.patch create mode 100644 lang/python/python3/patches/029-bpo-41288-Fix-a-crash-in-unpickling-invalid-NEWOBJ_EX-GH-21458-GH-21461.patch create mode 100644 lang/python/python3/patches/030-bpo-39017-Avoid-infinite-loop-in-the-tarfile-module-GH-21454-GH-21484.patch create mode 100644 lang/python/python3/patches/031-bpo-39603-Prevent-header-injection-in-http-methods-GH-18485-GH-21538.patch diff --git a/lang/python/python3/Makefile b/lang/python/python3/Makefile index 6403c1dc00..d9b66979fb 100644 --- a/lang/python/python3/Makefile +++ b/lang/python/python3/Makefile @@ -14,7 +14,7 @@ PYTHON_VERSION:=$(PYTHON3_VERSION) PYTHON_VERSION_MICRO:=$(PYTHON3_VERSION_MICRO) PKG_NAME:=python3 -PKG_RELEASE:=2 +PKG_RELEASE:=3 PKG_VERSION:=$(PYTHON_VERSION).$(PYTHON_VERSION_MICRO) PKG_SOURCE:=Python-$(PKG_VERSION).tar.xz diff --git a/lang/python/python3/patches/027-bpo-41004-Resolve-hash-collisions-for-IPv4Interface-and-IPv6Interface-GH-21033-GH-21231.patch b/lang/python/python3/patches/027-bpo-41004-Resolve-hash-collisions-for-IPv4Interface-and-IPv6Interface-GH-21033-GH-21231.patch new file mode 100644 index 0000000000..9042f832d4 --- /dev/null +++ b/lang/python/python3/patches/027-bpo-41004-Resolve-hash-collisions-for-IPv4Interface-and-IPv6Interface-GH-21033-GH-21231.patch @@ -0,0 +1,74 @@ +From b98e7790c77a4378ec4b1c71b84138cb930b69b7 Mon Sep 17 00:00:00 2001 +From: Tapas Kundu <39723251+tapakund@users.noreply.github.com> +Date: Wed, 1 Jul 2020 00:50:21 +0530 +Subject: [PATCH] [3.7] bpo-41004: Resolve hash collisions for IPv4Interface + and IPv6Interface (GH-21033) (GH-21231) + +CVE-2020-14422 +The __hash__() methods of classes IPv4Interface and IPv6Interface had issue +of generating constant hash values of 32 and 128 respectively causing hash collisions. +The fix uses the hash() function to generate hash values for the objects +instead of XOR operation +(cherry picked from commit b30ee26e366bf509b7538d79bfec6c6d38d53f28) + +Co-authored-by: Ravi Teja P + +Signed-off-by: Tapas Kundu +--- + Lib/ipaddress.py | 4 ++-- + Lib/test/test_ipaddress.py | 11 +++++++++++ + .../Security/2020-06-29-16-02-29.bpo-41004.ovF0KZ.rst | 1 + + 3 files changed, 14 insertions(+), 2 deletions(-) + create mode 100644 Misc/NEWS.d/next/Security/2020-06-29-16-02-29.bpo-41004.ovF0KZ.rst + +diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py +index 80249288d73ab..54882934c3dc1 100644 +--- a/Lib/ipaddress.py ++++ b/Lib/ipaddress.py +@@ -1442,7 +1442,7 @@ def __lt__(self, other): + return False + + def __hash__(self): +- return self._ip ^ self._prefixlen ^ int(self.network.network_address) ++ return hash((self._ip, self._prefixlen, int(self.network.network_address))) + + __reduce__ = _IPAddressBase.__reduce__ + +@@ -2088,7 +2088,7 @@ def __lt__(self, other): + return False + + def __hash__(self): +- return self._ip ^ self._prefixlen ^ int(self.network.network_address) ++ return hash((self._ip, self._prefixlen, int(self.network.network_address))) + + __reduce__ = _IPAddressBase.__reduce__ + +diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py +index 455b893fb126f..1fb6a929dc2d9 100644 +--- a/Lib/test/test_ipaddress.py ++++ b/Lib/test/test_ipaddress.py +@@ -2091,6 +2091,17 @@ def testsixtofour(self): + sixtofouraddr.sixtofour) + self.assertFalse(bad_addr.sixtofour) + ++ # issue41004 Hash collisions in IPv4Interface and IPv6Interface ++ def testV4HashIsNotConstant(self): ++ ipv4_address1 = ipaddress.IPv4Interface("1.2.3.4") ++ ipv4_address2 = ipaddress.IPv4Interface("2.3.4.5") ++ self.assertNotEqual(ipv4_address1.__hash__(), ipv4_address2.__hash__()) ++ ++ # issue41004 Hash collisions in IPv4Interface and IPv6Interface ++ def testV6HashIsNotConstant(self): ++ ipv6_address1 = ipaddress.IPv6Interface("2001:658:22a:cafe:200:0:0:1") ++ ipv6_address2 = ipaddress.IPv6Interface("2001:658:22a:cafe:200:0:0:2") ++ self.assertNotEqual(ipv6_address1.__hash__(), ipv6_address2.__hash__()) + + if __name__ == '__main__': + unittest.main() +diff --git a/Misc/NEWS.d/next/Security/2020-06-29-16-02-29.bpo-41004.ovF0KZ.rst b/Misc/NEWS.d/next/Security/2020-06-29-16-02-29.bpo-41004.ovF0KZ.rst +new file mode 100644 +index 0000000000000..f5a9db52fff52 +--- /dev/null ++++ b/Misc/NEWS.d/next/Security/2020-06-29-16-02-29.bpo-41004.ovF0KZ.rst +@@ -0,0 +1 @@ ++CVE-2020-14422: The __hash__() methods of ipaddress.IPv4Interface and ipaddress.IPv6Interface incorrectly generated constant hash values of 32 and 128 respectively. This resulted in always causing hash collisions. The fix uses hash() to generate hash values for the tuple of (address, mask length, network address). diff --git a/lang/python/python3/patches/028-closes-bpo-41235-Fix-the-error-handling-in-SSLContext.load_dh_params-GH-21389.patch b/lang/python/python3/patches/028-closes-bpo-41235-Fix-the-error-handling-in-SSLContext.load_dh_params-GH-21389.patch new file mode 100644 index 0000000000..90d87b31b4 --- /dev/null +++ b/lang/python/python3/patches/028-closes-bpo-41235-Fix-the-error-handling-in-SSLContext.load_dh_params-GH-21389.patch @@ -0,0 +1,40 @@ +From c8c818b0d73680516d5841597b705a1feeb42113 Mon Sep 17 00:00:00 2001 +From: "Miss Islington (bot)" + <31488909+miss-islington@users.noreply.github.com> +Date: Tue, 7 Jul 2020 21:55:36 -0700 +Subject: [PATCH] closes bpo-41235: Fix the error handling in + SSLContext.load_dh_params() (GH-21389) + +(cherry picked from commit aebc0495572c5bb85d2bd97d27cf93ab038b5a6a) + +Co-authored-by: Zackery Spytz +--- + .../next/Library/2020-07-07-21-56-26.bpo-41235.H2csMU.rst | 1 + + Modules/_ssl.c | 6 ++++-- + 2 files changed, 5 insertions(+), 2 deletions(-) + create mode 100644 Misc/NEWS.d/next/Library/2020-07-07-21-56-26.bpo-41235.H2csMU.rst + +diff --git a/Misc/NEWS.d/next/Library/2020-07-07-21-56-26.bpo-41235.H2csMU.rst b/Misc/NEWS.d/next/Library/2020-07-07-21-56-26.bpo-41235.H2csMU.rst +new file mode 100644 +index 0000000000000..c55275bb1c622 +--- /dev/null ++++ b/Misc/NEWS.d/next/Library/2020-07-07-21-56-26.bpo-41235.H2csMU.rst +@@ -0,0 +1 @@ ++Fix the error handling in :meth:`ssl.SSLContext.load_dh_params`. +diff --git a/Modules/_ssl.c b/Modules/_ssl.c +index 93cc529e796a0..719f8e8ca308d 100644 +--- a/Modules/_ssl.c ++++ b/Modules/_ssl.c +@@ -4189,8 +4189,10 @@ _ssl__SSLContext_load_dh_params(PySSLContext *self, PyObject *filepath) + } + return NULL; + } +- if (SSL_CTX_set_tmp_dh(self->ctx, dh) == 0) +- _setSSLError(NULL, 0, __FILE__, __LINE__); ++ if (!SSL_CTX_set_tmp_dh(self->ctx, dh)) { ++ DH_free(dh); ++ return _setSSLError(NULL, 0, __FILE__, __LINE__); ++ } + DH_free(dh); + Py_RETURN_NONE; + } diff --git a/lang/python/python3/patches/029-bpo-41288-Fix-a-crash-in-unpickling-invalid-NEWOBJ_EX-GH-21458-GH-21461.patch b/lang/python/python3/patches/029-bpo-41288-Fix-a-crash-in-unpickling-invalid-NEWOBJ_EX-GH-21458-GH-21461.patch new file mode 100644 index 0000000000..f36a7a127d --- /dev/null +++ b/lang/python/python3/patches/029-bpo-41288-Fix-a-crash-in-unpickling-invalid-NEWOBJ_EX-GH-21458-GH-21461.patch @@ -0,0 +1,111 @@ +From 620e276a8c1d53332fbf08d369be87f862b6949d Mon Sep 17 00:00:00 2001 +From: "Miss Islington (bot)" + <31488909+miss-islington@users.noreply.github.com> +Date: Mon, 13 Jul 2020 11:17:01 -0700 +Subject: [PATCH] bpo-41288: Fix a crash in unpickling invalid NEWOBJ_EX. + (GH-21458) (GH-21461) + +Automerge-Triggered-By: @tiran +(cherry picked from commit 4f309abf55f0e6f8950ac13d6ec83c22b8d47bf8) + +Co-authored-by: Serhiy Storchaka +--- + Lib/test/pickletester.py | 18 ++++++++++++ + .../2020-07-13-15-06-35.bpo-41288.8mn5P-.rst | 2 ++ + Modules/_pickle.c | 29 ++++++++++++++----- + 3 files changed, 41 insertions(+), 8 deletions(-) + create mode 100644 Misc/NEWS.d/next/Library/2020-07-13-15-06-35.bpo-41288.8mn5P-.rst + +diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py +index 1d88fcb859af8..c576d73349af8 100644 +--- a/Lib/test/pickletester.py ++++ b/Lib/test/pickletester.py +@@ -998,6 +998,24 @@ def test_compat_unpickle(self): + self.assertIs(type(unpickled), collections.UserDict) + self.assertEqual(unpickled, collections.UserDict({1: 2})) + ++ def test_bad_reduce(self): ++ self.assertEqual(self.loads(b'cbuiltins\nint\n)R.'), 0) ++ self.check_unpickling_error(TypeError, b'N)R.') ++ self.check_unpickling_error(TypeError, b'cbuiltins\nint\nNR.') ++ ++ def test_bad_newobj(self): ++ error = (pickle.UnpicklingError, TypeError) ++ self.assertEqual(self.loads(b'cbuiltins\nint\n)\x81.'), 0) ++ self.check_unpickling_error(error, b'cbuiltins\nlen\n)\x81.') ++ self.check_unpickling_error(error, b'cbuiltins\nint\nN\x81.') ++ ++ def test_bad_newobj_ex(self): ++ error = (pickle.UnpicklingError, TypeError) ++ self.assertEqual(self.loads(b'cbuiltins\nint\n)}\x92.'), 0) ++ self.check_unpickling_error(error, b'cbuiltins\nlen\n)}\x92.') ++ self.check_unpickling_error(error, b'cbuiltins\nint\nN}\x92.') ++ self.check_unpickling_error(error, b'cbuiltins\nint\n)N\x92.') ++ + def test_bad_stack(self): + badpickles = [ + b'.', # STOP +diff --git a/Misc/NEWS.d/next/Library/2020-07-13-15-06-35.bpo-41288.8mn5P-.rst b/Misc/NEWS.d/next/Library/2020-07-13-15-06-35.bpo-41288.8mn5P-.rst +new file mode 100644 +index 0000000000000..3c3adbabf16ff +--- /dev/null ++++ b/Misc/NEWS.d/next/Library/2020-07-13-15-06-35.bpo-41288.8mn5P-.rst +@@ -0,0 +1,2 @@ ++Unpickling invalid NEWOBJ_EX opcode with the C implementation raises now ++UnpicklingError instead of crashing. +diff --git a/Modules/_pickle.c b/Modules/_pickle.c +index ef83da02e2e41..329631d7e3b98 100644 +--- a/Modules/_pickle.c ++++ b/Modules/_pickle.c +@@ -5515,23 +5515,30 @@ load_newobj_ex(UnpicklerObject *self) + } + + if (!PyType_Check(cls)) { +- Py_DECREF(kwargs); +- Py_DECREF(args); + PyErr_Format(st->UnpicklingError, + "NEWOBJ_EX class argument must be a type, not %.200s", + Py_TYPE(cls)->tp_name); +- Py_DECREF(cls); +- return -1; ++ goto error; + } + + if (((PyTypeObject *)cls)->tp_new == NULL) { +- Py_DECREF(kwargs); +- Py_DECREF(args); +- Py_DECREF(cls); + PyErr_SetString(st->UnpicklingError, + "NEWOBJ_EX class argument doesn't have __new__"); +- return -1; ++ goto error; ++ } ++ if (!PyTuple_Check(args)) { ++ PyErr_Format(st->UnpicklingError, ++ "NEWOBJ_EX args argument must be a tuple, not %.200s", ++ Py_TYPE(args)->tp_name); ++ goto error; ++ } ++ if (!PyDict_Check(kwargs)) { ++ PyErr_Format(st->UnpicklingError, ++ "NEWOBJ_EX kwargs argument must be a dict, not %.200s", ++ Py_TYPE(kwargs)->tp_name); ++ goto error; + } ++ + obj = ((PyTypeObject *)cls)->tp_new((PyTypeObject *)cls, args, kwargs); + Py_DECREF(kwargs); + Py_DECREF(args); +@@ -5541,6 +5548,12 @@ load_newobj_ex(UnpicklerObject *self) + } + PDATA_PUSH(self->stack, obj, -1); + return 0; ++ ++error: ++ Py_DECREF(kwargs); ++ Py_DECREF(args); ++ Py_DECREF(cls); ++ return -1; + } + + static int diff --git a/lang/python/python3/patches/030-bpo-39017-Avoid-infinite-loop-in-the-tarfile-module-GH-21454-GH-21484.patch b/lang/python/python3/patches/030-bpo-39017-Avoid-infinite-loop-in-the-tarfile-module-GH-21454-GH-21484.patch new file mode 100644 index 0000000000..c8515551f1 --- /dev/null +++ b/lang/python/python3/patches/030-bpo-39017-Avoid-infinite-loop-in-the-tarfile-module-GH-21454-GH-21484.patch @@ -0,0 +1,59 @@ +From 79c6b602efc9a906c8496f3d5f4d54c54b48fa06 Mon Sep 17 00:00:00 2001 +From: "Miss Islington (bot)" + <31488909+miss-islington@users.noreply.github.com> +Date: Wed, 15 Jul 2020 05:35:08 -0700 +Subject: [PATCH] bpo-39017: Avoid infinite loop in the tarfile module + (GH-21454) (GH-21484) + +Avoid infinite loop when reading specially crafted TAR files using the tarfile module +(CVE-2019-20907). +(cherry picked from commit 5a8d121a1f3ef5ad7c105ee378cc79a3eac0c7d4) + +Co-authored-by: Rishi +--- + Lib/tarfile.py | 2 ++ + Lib/test/recursion.tar | Bin 0 -> 516 bytes + Lib/test/test_tarfile.py | 7 +++++++ + .../2020-07-12-22-16-58.bpo-39017.x3Cg-9.rst | 1 + + 4 files changed, 10 insertions(+) + create mode 100644 Lib/test/recursion.tar + create mode 100644 Misc/NEWS.d/next/Library/2020-07-12-22-16-58.bpo-39017.x3Cg-9.rst + +diff --git a/Lib/tarfile.py b/Lib/tarfile.py +index 3b596cbf49d27..3be5188c8b0a2 100755 +--- a/Lib/tarfile.py ++++ b/Lib/tarfile.py +@@ -1233,6 +1233,8 @@ def _proc_pax(self, tarfile): + + 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] + + # Normally, we could just use "utf-8" as the encoding and "strict" +diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py +index 5e4d75ecfce1a..9133d60e49be1 100644 +--- a/Lib/test/test_tarfile.py ++++ b/Lib/test/test_tarfile.py +@@ -395,6 +395,13 @@ def test_premature_end_of_archive(self): + with self.assertRaisesRegex(tarfile.ReadError, "unexpected end of data"): + tar.extractfile(t).read() + ++ def test_length_zero_header(self): ++ # bpo-39017 (CVE-2019-20907): reading a zero-length header should fail ++ # with an exception ++ with self.assertRaisesRegex(tarfile.ReadError, "file could not be opened successfully"): ++ with tarfile.open(support.findfile('recursion.tar')) as tar: ++ pass ++ + class MiscReadTestBase(CommonReadTest): + def requires_name_attribute(self): + pass +diff --git a/Misc/NEWS.d/next/Library/2020-07-12-22-16-58.bpo-39017.x3Cg-9.rst b/Misc/NEWS.d/next/Library/2020-07-12-22-16-58.bpo-39017.x3Cg-9.rst +new file mode 100644 +index 0000000000000..ad26676f8b856 +--- /dev/null ++++ b/Misc/NEWS.d/next/Library/2020-07-12-22-16-58.bpo-39017.x3Cg-9.rst +@@ -0,0 +1 @@ ++Avoid infinite loop when reading specially crafted TAR files using the tarfile module (CVE-2019-20907). diff --git a/lang/python/python3/patches/031-bpo-39603-Prevent-header-injection-in-http-methods-GH-18485-GH-21538.patch b/lang/python/python3/patches/031-bpo-39603-Prevent-header-injection-in-http-methods-GH-18485-GH-21538.patch new file mode 100644 index 0000000000..67d9040aad --- /dev/null +++ b/lang/python/python3/patches/031-bpo-39603-Prevent-header-injection-in-http-methods-GH-18485-GH-21538.patch @@ -0,0 +1,99 @@ +From ca75fec1ed358f7324272608ca952b2d8226d11a Mon Sep 17 00:00:00 2001 +From: "Miss Islington (bot)" + <31488909+miss-islington@users.noreply.github.com> +Date: Sun, 19 Jul 2020 02:27:35 -0700 +Subject: [PATCH] bpo-39603: Prevent header injection in http methods + (GH-18485) (GH-21538) + +reject control chars in http method in http.client.putrequest to prevent http header injection +(cherry picked from commit 8ca8a2e8fb068863c1138f07e3098478ef8be12e) + +Co-authored-by: AMIR <31338382+amiremohamadi@users.noreply.github.com> +--- + Lib/http/client.py | 15 +++++++++++++ + Lib/test/test_httplib.py | 22 +++++++++++++++++++ + .../2020-02-12-14-17-39.bpo-39603.Gt3RSg.rst | 2 ++ + 3 files changed, 39 insertions(+) + create mode 100644 Misc/NEWS.d/next/Security/2020-02-12-14-17-39.bpo-39603.Gt3RSg.rst + +diff --git a/Lib/http/client.py b/Lib/http/client.py +index 09c57af865ee0..04cd8f7d84986 100644 +--- a/Lib/http/client.py ++++ b/Lib/http/client.py +@@ -150,6 +150,10 @@ + # _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'} +@@ -1109,6 +1113,8 @@ def putrequest(self, method, url, skip_host=False, + else: + raise CannotSendRequest(self.__state) + ++ self._validate_method(method) ++ + # Save the method for use later in the response phase + self._method = method + +@@ -1199,6 +1205,15 @@ def _encode_request(self, request): + # ASCII also helps prevent CVE-2019-9740. + return request.encode('ascii') + ++ 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: ++ raise ValueError( ++ f"method can't contain control characters. {method!r} " ++ f"(found at least {match.group()!r})") ++ + def _validate_path(self, url): + """Validate a url for putrequest.""" + # Prevent CVE-2019-9740. +diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py +index 891393ab869e6..3fa0691d3ad8f 100644 +--- a/Lib/test/test_httplib.py ++++ b/Lib/test/test_httplib.py +@@ -363,6 +363,28 @@ def test_headers_debuglevel(self): + self.assertEqual(lines[3], "header: Second: val2") + + ++class HttpMethodTests(TestCase): ++ 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.assertRaisesRegex( ++ ValueError, "method can't contain control characters"): ++ conn = client.HTTPConnection('example.com') ++ conn.sock = FakeSocket(None) ++ conn.request(method=method, url="/") ++ ++ + class TransferEncodingTest(TestCase): + expected_body = b"It's just a flesh wound" + +diff --git a/Misc/NEWS.d/next/Security/2020-02-12-14-17-39.bpo-39603.Gt3RSg.rst b/Misc/NEWS.d/next/Security/2020-02-12-14-17-39.bpo-39603.Gt3RSg.rst +new file mode 100644 +index 0000000000000..990affc3edd9d +--- /dev/null ++++ b/Misc/NEWS.d/next/Security/2020-02-12-14-17-39.bpo-39603.Gt3RSg.rst +@@ -0,0 +1,2 @@ ++Prevent http header injection by rejecting control characters in ++http.client.putrequest(...). -- 2.30.2