ports/opt (3.6): [notify] python: patch 3 security issues: CVE-2019-20907, CVE-2020-26116, and CVE-2021-3177
commit 2e2843fede4d0086e93c9b5434b09c44318882c4 Author: Danny Rawlins <contact@romster.me> Date: Sat Feb 27 20:55:11 2021 +1100 [notify] python: patch 3 security issues: CVE-2019-20907, CVE-2020-26116, and CVE-2021-3177 diff --git a/python/.signature b/python/.signature index 97bd0a258..71c6b500a 100644 --- a/python/.signature +++ b/python/.signature @@ -1,6 +1,9 @@ untrusted comment: verify with /etc/ports/opt.pub -RWSE3ohX2g5d/a3K/I66yw0JReBFnr+BMNq9XxrSNdzGhoOvHro1VE9PpDKZ5W+6/jMv+GOb43/y/V28Qbhn8Bm96xWQJlj9AQs= -SHA256 (Pkgfile) = 0bdf8c849eb717a9fb4abaa0f71cf5f2d8dfa5a896740b594febf511f961681d +RWSE3ohX2g5d/SLClB71/t4Fok+TXEN5kW8zj7yJYxYSakqmWJY0a9ymGkcO7fRu33meHIdoa13OYWhUjNZS+zlEoMZChuQqIAc= +SHA256 (Pkgfile) = 4a021c82c4cfa9716295e41571af6f5aea29c8b40ec2003a65e9278833b43ef1 SHA256 (.footprint) = 45f6aea32375cc9cd313a77c570abcd08cbb4a9612d868eedf1e52d06ee062b4 SHA256 (Python-2.7.18.tar.xz) = b62c0e7937551d0cc02b8fd5cb0f544f9405bafc9a54d3808ed4594812edef43 SHA256 (pyconfig.h) = 081426cb9524c2e156a71bb035c25a67e44d389afc6f7e091bcf86a7f4e2002f +SHA256 (CVE-2019-20907.patch) = 59f8039b26f6a4613847fb4b30a1da612b4c0d5ed3c1cc92f498ebd71a734b04 +SHA256 (CVE-2020-26116.patch) = 290f34ab3e6cdd99fff00ab7b1e01d3e9c4acc36ecb87b7db76207c4cdaf692a +SHA256 (CVE-2021-3177.patch) = db7835b3fa3a2897e531a219127a02bbc902e31f3363f27a456a45ddbb4eca31 diff --git a/python/CVE-2019-20907.patch b/python/CVE-2019-20907.patch new file mode 100644 index 000000000..64da85855 --- /dev/null +++ b/python/CVE-2019-20907.patch @@ -0,0 +1,25 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Rishi <rishi_devan@mail.com> +Date: Wed, 15 Jul 2020 13:51:00 +0200 +Subject: [PATCH] 00351-cve-2019-20907-fix-infinite-loop-in-tarfile.patch + +00351 # +Avoid infinite loop when reading specially crafted TAR files using the tarfile module +(CVE-2019-20907). +See: https://bugs.python.org/issue39017 +--- + Lib/tarfile.py | 2 ++ + +diff --git a/Lib/tarfile.py b/Lib/tarfile.py +index adf91d53823..574a6bb279d 100644 +--- a/Lib/tarfile.py ++++ b/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 --git a/python/CVE-2020-26116.patch b/python/CVE-2020-26116.patch new file mode 100644 index 000000000..c13655e2e --- /dev/null +++ b/python/CVE-2020-26116.patch @@ -0,0 +1,113 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AMIR <31338382+amiremohamadi@users.noreply.github.com> +Date: Sun, 19 Jul 2020 00:46:10 +0430 +Subject: [PATCH] + 00354-cve-2020-26116-http-request-method-crlf-injection-in-httplib.patch + +00354 # +Reject control chars in HTTP method in httplib.putrequest to prevent +HTTP header injection + +Backported from Python 3.5-3.10 (and adjusted for py2's single-module httplib): +- https://bugs.python.org/issue39603 +- https://github.com/python/cpython/pull/18485 (3.10) +- https://github.com/python/cpython/pull/21946 (3.5) + +Co-authored-by: AMIR <31338382+amiremohamadi@users.noreply.github.com> +--- + Lib/httplib.py | 16 +++++++++++++ + Lib/test/test_httplib.py | 23 +++++++++++++++++++ + .../2020-02-12-14-17-39.bpo-39603.Gt3RSg.rst | 2 ++ + 3 files changed, 41 insertions(+) + create mode 100644 Misc/NEWS.d/next/Security/2020-02-12-14-17-39.bpo-39603.Gt3RSg.rst + +diff --git a/Lib/httplib.py b/Lib/httplib.py +index fcc4152aaf2..a63677477d5 100644 +--- a/Lib/httplib.py ++++ b/Lib/httplib.py +@@ -257,6 +257,10 @@ _contains_disallowed_url_pchar_re = re.compile('[\x00-\x20\x7f-\xff]') + # _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,16 @@ 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: ++ raise ValueError( ++ "method can't contain control characters. %r " ++ "(found at least %r)" ++ % (method, match.group())) ++ + 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 d8a57f73530..e295bb796ec 100644 +--- a/Lib/test/test_httplib.py ++++ b/Lib/test/test_httplib.py +@@ -385,6 +385,28 @@ class HeaderTests(TestCase): + conn.putheader(name, value) + + ++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.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): + # Test HTTP status lines +@@ -1010,6 +1032,7 @@ class TunnelTests(TestCase): + @test_support.reap_threads + def test_main(verbose=None): + test_support.run_unittest(HeaderTests, OfflineTest, BasicTest, TimeoutTest, ++ HttpMethodTests, + HTTPTest, HTTPSTest, SourceAddressTest, + TunnelTests) + +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 00000000000..990affc3edd +--- /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(...). diff --git a/python/CVE-2021-3177.patch b/python/CVE-2021-3177.patch new file mode 100644 index 000000000..09be782c0 --- /dev/null +++ b/python/CVE-2021-3177.patch @@ -0,0 +1,179 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Petr Viktorin <pviktori@redhat.com> +Date: Mon, 1 Feb 2021 19:29:17 +0100 +Subject: [PATCH] 00357-CVE-2021-3177.patch + +00357 # +CVE-2021-3177: Replace snprintf with Python unicode formatting in ctypes param reprs + +Backport of Python3 commit 916610ef90a0d0761f08747f7b0905541f0977c7: +https://bugs.python.org/issue42938 +https://github.com/python/cpython/pull/24239 +--- + Lib/ctypes/test/test_parameters.py | 43 ++++++++++++++++++++ + Modules/_ctypes/callproc.c | 65 +++++++++++++++++------------- + 2 files changed, 80 insertions(+), 28 deletions(-) + +diff --git a/Lib/ctypes/test/test_parameters.py b/Lib/ctypes/test/test_parameters.py +index 23c1b6e2259..77300d71ae1 100644 +--- a/Lib/ctypes/test/test_parameters.py ++++ b/Lib/ctypes/test/test_parameters.py +@@ -206,6 +206,49 @@ class SimpleTypesTestCase(unittest.TestCase): + with self.assertRaises(ZeroDivisionError): + WorseStruct().__setstate__({}, b'foo') + ++ def test_parameter_repr(self): ++ from ctypes import ( ++ c_bool, ++ c_char, ++ c_wchar, ++ c_byte, ++ c_ubyte, ++ c_short, ++ c_ushort, ++ c_int, ++ c_uint, ++ c_long, ++ c_ulong, ++ c_longlong, ++ c_ulonglong, ++ c_float, ++ c_double, ++ c_longdouble, ++ c_char_p, ++ c_wchar_p, ++ c_void_p, ++ ) ++ self.assertRegexpMatches(repr(c_bool.from_param(True)), r"^<cparam '\?' at 0x[A-Fa-f0-9]+>$") ++ self.assertEqual(repr(c_char.from_param('a')), "<cparam 'c' ('a')>") ++ self.assertRegexpMatches(repr(c_wchar.from_param('a')), r"^<cparam 'u' at 0x[A-Fa-f0-9]+>$") ++ self.assertEqual(repr(c_byte.from_param(98)), "<cparam 'b' (98)>") ++ self.assertEqual(repr(c_ubyte.from_param(98)), "<cparam 'B' (98)>") ++ self.assertEqual(repr(c_short.from_param(511)), "<cparam 'h' (511)>") ++ self.assertEqual(repr(c_ushort.from_param(511)), "<cparam 'H' (511)>") ++ self.assertRegexpMatches(repr(c_int.from_param(20000)), r"^<cparam '[li]' \(20000\)>$") ++ self.assertRegexpMatches(repr(c_uint.from_param(20000)), r"^<cparam '[LI]' \(20000\)>$") ++ self.assertRegexpMatches(repr(c_long.from_param(20000)), r"^<cparam '[li]' \(20000\)>$") ++ self.assertRegexpMatches(repr(c_ulong.from_param(20000)), r"^<cparam '[LI]' \(20000\)>$") ++ self.assertRegexpMatches(repr(c_longlong.from_param(20000)), r"^<cparam '[liq]' \(20000\)>$") ++ self.assertRegexpMatches(repr(c_ulonglong.from_param(20000)), r"^<cparam '[LIQ]' \(20000\)>$") ++ self.assertEqual(repr(c_float.from_param(1.5)), "<cparam 'f' (1.5)>") ++ self.assertEqual(repr(c_double.from_param(1.5)), "<cparam 'd' (1.5)>") ++ self.assertEqual(repr(c_double.from_param(1e300)), "<cparam 'd' (1e+300)>") ++ self.assertRegexpMatches(repr(c_longdouble.from_param(1.5)), r"^<cparam ('d' \(1.5\)|'g' at 0x[A-Fa-f0-9]+)>$") ++ self.assertRegexpMatches(repr(c_char_p.from_param(b'hihi')), "^<cparam 'z' \(0x[A-Fa-f0-9]+\)>$") ++ self.assertRegexpMatches(repr(c_wchar_p.from_param('hihi')), "^<cparam 'Z' \(0x[A-Fa-f0-9]+\)>$") ++ self.assertRegexpMatches(repr(c_void_p.from_param(0x12)), r"^<cparam 'P' \(0x0*12\)>$") ++ + ################################################################ + + if __name__ == '__main__': +diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c +index 066fefc0cca..5cc3c4cf685 100644 +--- a/Modules/_ctypes/callproc.c ++++ b/Modules/_ctypes/callproc.c +@@ -460,50 +460,62 @@ PyCArg_dealloc(PyCArgObject *self) + static PyObject * + PyCArg_repr(PyCArgObject *self) + { +- char buffer[256]; + switch(self->tag) { + case 'b': + case 'B': +- sprintf(buffer, "<cparam '%c' (%d)>", ++ return PyString_FromFormat("<cparam '%c' (%d)>", + self->tag, self->value.b); +- break; + case 'h': + case 'H': +- sprintf(buffer, "<cparam '%c' (%d)>", ++ return PyString_FromFormat("<cparam '%c' (%d)>", + self->tag, self->value.h); +- break; + case 'i': + case 'I': +- sprintf(buffer, "<cparam '%c' (%d)>", ++ return PyString_FromFormat("<cparam '%c' (%d)>", + self->tag, self->value.i); +- break; + case 'l': + case 'L': +- sprintf(buffer, "<cparam '%c' (%ld)>", ++ return PyString_FromFormat("<cparam '%c' (%ld)>", + self->tag, self->value.l); +- break; + + #ifdef HAVE_LONG_LONG + case 'q': + case 'Q': +- sprintf(buffer, +- "<cparam '%c' (%" PY_FORMAT_LONG_LONG "d)>", ++ return PyString_FromFormat("<cparam '%c' (%lld)>", + self->tag, self->value.q); +- break; + #endif + case 'd': +- sprintf(buffer, "<cparam '%c' (%f)>", +- self->tag, self->value.d); +- break; +- case 'f': +- sprintf(buffer, "<cparam '%c' (%f)>", +- self->tag, self->value.f); +- break; +- ++ case 'f': { ++ PyObject *s = PyString_FromFormat("<cparam '%c' (", self->tag); ++ if (s == NULL) { ++ return NULL; ++ } ++ PyObject *f = PyFloat_FromDouble((self->tag == 'f') ? self->value.f : self->value.d); ++ if (f == NULL) { ++ Py_DECREF(s); ++ return NULL; ++ } ++ PyObject *r = PyObject_Repr(f); ++ Py_DECREF(f); ++ if (r == NULL) { ++ Py_DECREF(s); ++ return NULL; ++ } ++ PyString_ConcatAndDel(&s, r); ++ if (s == NULL) { ++ return NULL; ++ } ++ r = PyString_FromString(")>"); ++ if (r == NULL) { ++ Py_DECREF(s); ++ return NULL; ++ } ++ PyString_ConcatAndDel(&s, r); ++ return s; ++ } + case 'c': +- sprintf(buffer, "<cparam '%c' (%c)>", ++ return PyString_FromFormat("<cparam '%c' ('%c')>", + self->tag, self->value.c); +- break; + + /* Hm, are these 'z' and 'Z' codes useful at all? + Shouldn't they be replaced by the functionality of c_string +@@ -512,16 +524,13 @@ PyCArg_repr(PyCArgObject *self) + case 'z': + case 'Z': + case 'P': +- sprintf(buffer, "<cparam '%c' (%p)>", ++ return PyUnicode_FromFormat("<cparam '%c' (%p)>", + self->tag, self->value.p); +- break; + + default: +- sprintf(buffer, "<cparam '%c' at %p>", +- self->tag, self); +- break; ++ return PyString_FromFormat("<cparam '%c' at %p>", ++ (unsigned char)self->tag, (void *)self); + } +- return PyString_FromString(buffer); + } + + static PyMemberDef PyCArgType_members[] = { diff --git a/python/Pkgfile b/python/Pkgfile index 558029c06..6618bbf6e 100644 --- a/python/Pkgfile +++ b/python/Pkgfile @@ -5,13 +5,20 @@ name=python version=2.7.18 -release=2 +release=3 source=(https://www.python.org/ftp/$name/${version::6}/Python-$version.tar.xz \ - pyconfig.h) + pyconfig.h \ + CVE-2019-20907.patch \ + CVE-2020-26116.patch \ + CVE-2021-3177.patch) build () { cd Python-$version + patch -p1 -i $SRC/CVE-2019-20907.patch + patch -p1 -i $SRC/CVE-2020-26116.patch + patch -p1 -i $SRC/CVE-2021-3177.patch + # remove 2to3, we use the one from python3 rm -r Lib/lib2to3
participants (1)
-
crux@crux.nu