From 4f815b32519a8baf274abc03c8c6b2d3fb5cdb46308f2ee94b9697ecfecc2d6e Mon Sep 17 00:00:00 2001 From: Jan Matejek Date: Mon, 10 Feb 2014 14:35:47 +0000 Subject: [PATCH] - added patches for CVE-2013-1752 (bnc#856836) issues that are missing in 2.7.6: python-2.7.6-imaplib.patch python-2.7.6-poplib.patch smtplib_maxline-2.7.patch - CVE-2013-1753 (bnc#856835) gzip decompression bomb in xmlrpc client: xmlrpc_gzip_27.patch OBS-URL: https://build.opensuse.org/package/show/devel:languages:python:Factory/python?expand=0&rev=159 --- python-2.7.6-imaplib.patch | 59 ++++++++++++++++++ python-2.7.6-poplib.patch | 63 +++++++++++++++++++ python-base.changes | 11 ++++ python-base.spec | 10 +++ python-doc.spec | 11 +++- python.spec | 11 +++- smtplib_maxline-2.7.patch | 92 +++++++++++++++++++++++++++ xmlrpc_gzip_27.patch | 124 +++++++++++++++++++++++++++++++++++++ 8 files changed, 379 insertions(+), 2 deletions(-) create mode 100644 python-2.7.6-imaplib.patch create mode 100644 python-2.7.6-poplib.patch create mode 100644 smtplib_maxline-2.7.patch create mode 100644 xmlrpc_gzip_27.patch diff --git a/python-2.7.6-imaplib.patch b/python-2.7.6-imaplib.patch new file mode 100644 index 0000000..5af60ab --- /dev/null +++ b/python-2.7.6-imaplib.patch @@ -0,0 +1,59 @@ + +# HG changeset patch +# User R David Murray +# Date 1388775562 18000 +# Node ID dd906f4ab9237020a7a275c2d361fa288e553481 +# Parent 69b5f692455306c98aa27ecea17e6290787ebd3f +closes 16039: CVE-2013-1752: limit line length in imaplib readline calls. + +diff --git a/Lib/imaplib.py b/Lib/imaplib.py +--- a/Lib/imaplib.py ++++ b/Lib/imaplib.py +@@ -35,6 +35,15 @@ IMAP4_PORT = 143 + IMAP4_SSL_PORT = 993 + AllowedVersions = ('IMAP4REV1', 'IMAP4') # Most recent first + ++# Maximal line length when calling readline(). This is to prevent ++# reading arbitrary length lines. RFC 3501 and 2060 (IMAP 4rev1) ++# don't specify a line length. RFC 2683 however suggests limiting client ++# command lines to 1000 octets and server command lines to 8000 octets. ++# We have selected 10000 for some extra margin and since that is supposedly ++# also what UW and Panda IMAP does. ++_MAXLINE = 10000 ++ ++ + # Commands + + Commands = { +@@ -237,7 +246,10 @@ class IMAP4: + + def readline(self): + """Read line from remote.""" +- return self.file.readline() ++ line = self.file.readline(_MAXLINE + 1) ++ if len(line) > _MAXLINE: ++ raise self.error("got more than %d bytes" % _MAXLINE) ++ return line + + + def send(self, data): +diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py +--- a/Lib/test/test_imaplib.py ++++ b/Lib/test/test_imaplib.py +@@ -165,6 +165,16 @@ class BaseThreadedNetworkedTests(unittes + self.imap_class, *server.server_address) + + ++ def test_linetoolong(self): ++ class TooLongHandler(SimpleIMAPHandler): ++ def handle(self): ++ # Send a very long response line ++ self.wfile.write('* OK ' + imaplib._MAXLINE*'x' + '\r\n') ++ ++ with self.reaped_server(TooLongHandler) as server: ++ self.assertRaises(imaplib.IMAP4.error, ++ self.imap_class, *server.server_address) ++ + class ThreadedNetworkedTests(BaseThreadedNetworkedTests): + + server_class = SocketServer.TCPServer diff --git a/python-2.7.6-poplib.patch b/python-2.7.6-poplib.patch new file mode 100644 index 0000000..cc2df7b --- /dev/null +++ b/python-2.7.6-poplib.patch @@ -0,0 +1,63 @@ + +# HG changeset patch +# User Georg Brandl +# Date 1382855033 -3600 +# Node ID 68029048c9c6833b71c3121e5178f7f57f21b565 +# Parent 10d0edadbcddfd983c2c6c22d06c5a535197f8bf +Issue #16041: CVE-2013-1752: poplib: Limit maximum line lengths to 2048 to +prevent readline() calls from consuming too much memory. Patch by Jyrki +Pulliainen. + +Index: Python-2.7.6/Lib/poplib.py +=================================================================== +--- Python-2.7.6.orig/Lib/poplib.py 2013-11-10 08:36:40.000000000 +0100 ++++ Python-2.7.6/Lib/poplib.py 2014-02-07 18:45:45.454259311 +0100 +@@ -32,6 +32,12 @@ + LF = '\n' + CRLF = CR+LF + ++# maximal line length when calling readline(). This is to prevent ++# reading arbitrary lenght lines. RFC 1939 limits POP3 line length to ++# 512 characters, including CRLF. We have selected 2048 just to be on ++# the safe side. ++_MAXLINE = 2048 ++ + + class POP3: + +@@ -103,7 +109,10 @@ + # Raise error_proto('-ERR EOF') if the connection is closed. + + def _getline(self): +- line = self.file.readline() ++ line = self.file.readline(_MAXLINE + 1) ++ if len(line) > _MAXLINE: ++ raise error_proto('line too long') ++ + if self._debugging > 1: print '*get*', repr(line) + if not line: raise error_proto('-ERR EOF') + octets = len(line) +Index: Python-2.7.6/Lib/test/test_poplib.py +=================================================================== +--- Python-2.7.6.orig/Lib/test/test_poplib.py 2013-11-10 08:36:40.000000000 +0100 ++++ Python-2.7.6/Lib/test/test_poplib.py 2014-02-07 18:44:24.419856656 +0100 +@@ -81,7 +81,7 @@ + + def cmd_list(self, arg): + if arg: +- self.push('+OK %s %s' %(arg, arg)) ++ self.push('+OK %s %s' % (arg, arg)) + else: + self.push('+OK') + asynchat.async_chat.push(self, LIST_RESP) +@@ -198,6 +198,10 @@ + 113) + self.assertEqual(self.client.retr('foo'), expected) + ++ def test_too_long_lines(self): ++ self.assertRaises(poplib.error_proto, self.client._shortcmd, ++ 'echo +%s' % ((poplib._MAXLINE + 10) * 'a')) ++ + def test_dele(self): + self.assertOK(self.client.dele('foo')) + diff --git a/python-base.changes b/python-base.changes index 3bcf71d..6c0df6d 100644 --- a/python-base.changes +++ b/python-base.changes @@ -1,3 +1,14 @@ +------------------------------------------------------------------- +Mon Feb 10 14:24:52 UTC 2014 - jmatejek@suse.com + +- added patches for CVE-2013-1752 (bnc#856836) issues that are + missing in 2.7.6: + python-2.7.6-imaplib.patch + python-2.7.6-poplib.patch + smtplib_maxline-2.7.patch +- CVE-2013-1753 (bnc#856835) gzip decompression bomb in xmlrpc client: + xmlrpc_gzip_27.patch + ------------------------------------------------------------------- Tue Dec 10 16:56:02 UTC 2013 - uweigand@de.ibm.com diff --git a/python-base.spec b/python-base.spec index 3c1445f..2cfc9f3 100644 --- a/python-base.spec +++ b/python-base.spec @@ -52,6 +52,12 @@ Patch23: python-2.7.4-no-REUSEPORT.patch Patch24: python-bsddb6.diff # PATCH-FIX-OPENSUSE Properly support ppc64le in _ctypes module Patch25: libffi-ppc64le.diff +# CVE-2013-1753 [bnc#856835] unbounded gzip decompression in xmlrpc client +Patch26: xmlrpc_gzip_27.patch +# CVE-2013-1752 patches missing in 2.7.6: imaplib, poplib, smtplib +Patch27: python-2.7.6-imaplib.patch +Patch28: smtplib_maxline-2.7.patch +Patch29: python-2.7.6-poplib.patch # COMMON-PATCH-END %define python_version %(echo %{tarversion} | head -c 3) BuildRequires: automake @@ -146,6 +152,10 @@ other applications. %patch23 -p1 %patch24 -p1 %patch25 -p0 +%patch26 -p1 +%patch27 -p1 +%patch28 -p1 +%patch29 -p1 # drop Autoconf version requirement sed -i 's/^version_required/dnl version_required/' configure.ac diff --git a/python-doc.spec b/python-doc.spec index 7ec1f76..35ea0a4 100644 --- a/python-doc.spec +++ b/python-doc.spec @@ -15,7 +15,6 @@ # Please submit bugfixes or comments via http://bugs.opensuse.org/ # - Name: python-doc Version: 2.7.6 Release: 0 @@ -58,6 +57,12 @@ Patch23: python-2.7.4-no-REUSEPORT.patch Patch24: python-bsddb6.diff # PATCH-FIX-OPENSUSE Properly support ppc64le in _ctypes module Patch25: libffi-ppc64le.diff +# CVE-2013-1753 [bnc#856835] unbounded gzip decompression in xmlrpc client +Patch26: xmlrpc_gzip_27.patch +# CVE-2013-1752 patches missing in 2.7.6: imaplib, poplib, smtplib +Patch27: python-2.7.6-imaplib.patch +Patch28: smtplib_maxline-2.7.patch +Patch29: python-2.7.6-poplib.patch # COMMON-PATCH-END Provides: pyth_doc Provides: pyth_ps @@ -106,6 +111,10 @@ Python, and Macintosh Module Reference in PDF format. %patch23 -p1 %patch24 -p1 %patch25 -p0 +%patch26 -p1 +%patch27 -p1 +%patch28 -p1 +%patch29 -p1 # drop Autoconf version requirement sed -i 's/^version_required/dnl version_required/' configure.ac diff --git a/python.spec b/python.spec index 5965a90..f5d6b1e 100644 --- a/python.spec +++ b/python.spec @@ -15,7 +15,6 @@ # Please submit bugfixes or comments via http://bugs.opensuse.org/ # - Name: python Version: 2.7.6 Release: 0 @@ -59,6 +58,12 @@ Patch23: python-2.7.4-no-REUSEPORT.patch Patch24: python-bsddb6.diff # PATCH-FIX-OPENSUSE Properly support ppc64le in _ctypes module Patch25: libffi-ppc64le.diff +# CVE-2013-1753 [bnc#856835] unbounded gzip decompression in xmlrpc client +Patch26: xmlrpc_gzip_27.patch +# CVE-2013-1752 patches missing in 2.7.6: imaplib, poplib, smtplib +Patch27: python-2.7.6-imaplib.patch +Patch28: smtplib_maxline-2.7.patch +Patch29: python-2.7.6-poplib.patch # COMMON-PATCH-END BuildRequires: automake BuildRequires: db-devel @@ -183,6 +188,10 @@ implementation of the standard Unix DBM databases. %patch23 -p1 %patch24 -p1 %patch25 -p0 +%patch26 -p1 +%patch27 -p1 +%patch28 -p1 +%patch29 -p1 # drop Autoconf version requirement sed -i 's/^version_required/dnl version_required/' configure.ac diff --git a/smtplib_maxline-2.7.patch b/smtplib_maxline-2.7.patch new file mode 100644 index 0000000..44cd8b3 --- /dev/null +++ b/smtplib_maxline-2.7.patch @@ -0,0 +1,92 @@ +diff -r 44ac81e6d584 Lib/smtplib.py +--- a/Lib/smtplib.py Sun Oct 20 16:57:07 2013 +0300 ++++ b/Lib/smtplib.py Sun Oct 20 17:44:15 2013 +0300 +@@ -57,6 +57,7 @@ + SMTP_PORT = 25 + SMTP_SSL_PORT = 465 + CRLF = "\r\n" ++_MAXLINE = 8192 # more than 8 times larger than RFC 821, 4.5.3 + + OLDSTYLE_AUTH = re.compile(r"auth=(.*)", re.I) + +@@ -179,10 +180,14 @@ + def __init__(self, sslobj): + self.sslobj = sslobj + +- def readline(self): ++ def readline(self, size=-1): ++ if size < 0: ++ size = None + str = "" + chr = None + while chr != "\n": ++ if size is not None and len(str) >= size: ++ break + chr = self.sslobj.read(1) + if not chr: + break +@@ -353,7 +358,7 @@ + self.file = self.sock.makefile('rb') + while 1: + try: +- line = self.file.readline() ++ line = self.file.readline(_MAXLINE + 1) + except socket.error as e: + self.close() + raise SMTPServerDisconnected("Connection unexpectedly closed: " +@@ -363,6 +368,8 @@ + raise SMTPServerDisconnected("Connection unexpectedly closed") + if self.debuglevel > 0: + print>>stderr, 'reply:', repr(line) ++ if len(line) > _MAXLINE: ++ raise SMTPResponseException(500, "Line too long.") + resp.append(line[4:].strip()) + code = line[:3] + # Check that the error code is syntactically correct. +diff -r 44ac81e6d584 Lib/test/test_smtplib.py +--- a/Lib/test/test_smtplib.py Sun Oct 20 16:57:07 2013 +0300 ++++ b/Lib/test/test_smtplib.py Sun Oct 20 17:44:15 2013 +0300 +@@ -292,6 +292,33 @@ + HOST, self.port, 'localhost', 3) + + ++@unittest.skipUnless(threading, 'Threading required for this test.') ++class TooLongLineTests(unittest.TestCase): ++ respdata = '250 OK' + ('.' * smtplib._MAXLINE * 2) + '\n' ++ ++ def setUp(self): ++ self.old_stdout = sys.stdout ++ self.output = StringIO.StringIO() ++ sys.stdout = self.output ++ ++ self.evt = threading.Event() ++ self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ++ self.sock.settimeout(15) ++ self.port = test_support.bind_port(self.sock) ++ servargs = (self.evt, self.respdata, self.sock) ++ threading.Thread(target=server, args=servargs).start() ++ self.evt.wait() ++ self.evt.clear() ++ ++ def tearDown(self): ++ self.evt.wait() ++ sys.stdout = self.old_stdout ++ ++ def testLineTooLong(self): ++ self.assertRaises(smtplib.SMTPResponseException, smtplib.SMTP, ++ HOST, self.port, 'localhost', 3) ++ ++ + sim_users = {'Mr.A@somewhere.com':'John A', + 'Ms.B@somewhere.com':'Sally B', + 'Mrs.C@somewhereesle.com':'Ruth C', +@@ -511,7 +538,8 @@ + def test_main(verbose=None): + test_support.run_unittest(GeneralTests, DebuggingServerTests, + NonConnectingTests, +- BadHELOServerTests, SMTPSimTests) ++ BadHELOServerTests, SMTPSimTests, ++ TooLongLineTests) + + if __name__ == '__main__': + test_main() diff --git a/xmlrpc_gzip_27.patch b/xmlrpc_gzip_27.patch new file mode 100644 index 0000000..d0f9ac4 --- /dev/null +++ b/xmlrpc_gzip_27.patch @@ -0,0 +1,124 @@ +diff --git a/Doc/library/xmlrpclib.rst b/Doc/library/xmlrpclib.rst +--- a/Doc/library/xmlrpclib.rst ++++ b/Doc/library/xmlrpclib.rst +@@ -120,6 +120,15 @@ + *__dict__* attribute and don't have a base class that is marshalled in a + special way. + ++.. data:: MAX_GZIP_DECODE ++ ++ The module constant specifies the amount of bytes that are decompressed by ++ :func:`gzip_decode`. The default value is *20 MB*. A value of *-1* disables ++ the protection. ++ ++ .. versionadded:: 2.7.4 ++ The constant was added to strengthen the module against gzip bomb ++ attacks. + + .. seealso:: + +diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py +--- a/Lib/test/test_xmlrpc.py ++++ b/Lib/test/test_xmlrpc.py +@@ -19,6 +19,11 @@ + threading = None + + try: ++ import gzip ++except ImportError: ++ gzip = None ++ ++try: + unicode + except NameError: + have_unicode = False +@@ -731,7 +736,7 @@ + with cm: + p.pow(6, 8) + +- def test_gsip_response(self): ++ def test_gzip_response(self): + t = self.Transport() + p = xmlrpclib.ServerProxy(URL, transport=t) + old = self.requestHandler.encode_threshold +@@ -744,6 +749,27 @@ + self.requestHandler.encode_threshold = old + self.assertTrue(a>b) + ++ def test_gzip_decode_limit(self): ++ data = '\0' * xmlrpclib.MAX_GZIP_DECODE ++ encoded = xmlrpclib.gzip_encode(data) ++ decoded = xmlrpclib.gzip_decode(encoded) ++ self.assertEqual(len(decoded), xmlrpclib.MAX_GZIP_DECODE) ++ ++ data = '\0' * (xmlrpclib.MAX_GZIP_DECODE + 1) ++ encoded = xmlrpclib.gzip_encode(data) ++ ++ with self.assertRaisesRegexp(ValueError, ++ "max gzipped payload length exceeded"): ++ xmlrpclib.gzip_decode(encoded) ++ ++ oldmax = xmlrpclib.MAX_GZIP_DECODE ++ try: ++ xmlrpclib.MAX_GZIP_DECODE = -1 ++ xmlrpclib.gzip_decode(encoded) ++ finally: ++ xmlrpclib.MAX_GZIP_DECODE = oldmax ++ ++ + #Test special attributes of the ServerProxy object + class ServerProxyTestCase(unittest.TestCase): + def setUp(self): +@@ -1011,11 +1037,8 @@ + xmlrpc_tests.append(SimpleServerTestCase) + xmlrpc_tests.append(KeepaliveServerTestCase1) + xmlrpc_tests.append(KeepaliveServerTestCase2) +- try: +- import gzip ++ if gzip is not None: + xmlrpc_tests.append(GzipServerTestCase) +- except ImportError: +- pass #gzip not supported in this build + xmlrpc_tests.append(MultiPathServerTestCase) + xmlrpc_tests.append(ServerProxyTestCase) + xmlrpc_tests.append(FailingServerTestCase) +diff --git a/Lib/xmlrpclib.py b/Lib/xmlrpclib.py +--- a/Lib/xmlrpclib.py ++++ b/Lib/xmlrpclib.py +@@ -49,6 +49,7 @@ + # 2003-07-12 gp Correct marshalling of Faults + # 2003-10-31 mvl Add multicall support + # 2004-08-20 mvl Bump minimum supported Python version to 2.1 ++# 2013-01-20 ch Add workaround for gzip bomb vulnerability + # + # Copyright (c) 1999-2002 by Secret Labs AB. + # Copyright (c) 1999-2002 by Fredrik Lundh. +@@ -147,6 +148,10 @@ + except ImportError: + gzip = None #python can be built without zlib/gzip support + ++# Limit the maximum amount of decoded data that is decompressed. The ++# limit prevents gzip bomb attacks. ++MAX_GZIP_DECODE = 20 * 1024 * 1024 # 20 MB ++ + # -------------------------------------------------------------------- + # Internal stuff + +@@ -1178,11 +1183,16 @@ + f = StringIO.StringIO(data) + gzf = gzip.GzipFile(mode="rb", fileobj=f) + try: +- decoded = gzf.read() ++ if MAX_GZIP_DECODE < 0: # no limit ++ decoded = gzf.read() ++ else: ++ decoded = gzf.read(MAX_GZIP_DECODE + 1) + except IOError: + raise ValueError("invalid data") + f.close() + gzf.close() ++ if MAX_GZIP_DECODE >= 0 and len(decoded) > MAX_GZIP_DECODE: ++ raise ValueError("max gzipped payload length exceeded") + return decoded + + ##