- 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
This commit is contained in:
Jan Matejek 2014-02-10 14:35:47 +00:00 committed by Git OBS Bridge
parent 73549b66f0
commit 4f815b3251
8 changed files with 379 additions and 2 deletions

View File

@ -0,0 +1,59 @@
# HG changeset patch
# User R David Murray <rdmurray@bitdance.com>
# 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

63
python-2.7.6-poplib.patch Normal file
View File

@ -0,0 +1,63 @@
# HG changeset patch
# User Georg Brandl <georg@python.org>
# 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'))

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

92
smtplib_maxline-2.7.patch Normal file
View File

@ -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()

124
xmlrpc_gzip_27.patch Normal file
View File

@ -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
##