From 15f68138d4d0ff56704217369facc8baf03783a5 Mon Sep 17 00:00:00 2001 From: Tudor Florea Date: Tue, 22 Sep 2015 01:38:33 +0200 Subject: python: Backport CVE-2013-1752 fix from upstream Signed-off-by: Tudor Florea --- .../python-2.7.3-CVE-2013-1752-ftplib-fix.patch | 149 +++++++++++++++++++++ .../python-2.7.3-CVE-2013-1752-imaplib-fix.patch | 37 +++++ .../python-2.7.3-CVE-2013-1752-nntplib-fix.patch | 105 +++++++++++++++ .../python-2.7.3-CVE-2013-1752-poplib-fix.patch | 90 +++++++++++++ meta/recipes-devtools/python/python_2.7.3.bb | 4 + 5 files changed, 385 insertions(+) create mode 100644 meta/recipes-devtools/python/python/python-2.7.3-CVE-2013-1752-ftplib-fix.patch create mode 100644 meta/recipes-devtools/python/python/python-2.7.3-CVE-2013-1752-imaplib-fix.patch create mode 100644 meta/recipes-devtools/python/python/python-2.7.3-CVE-2013-1752-nntplib-fix.patch create mode 100644 meta/recipes-devtools/python/python/python-2.7.3-CVE-2013-1752-poplib-fix.patch (limited to 'meta') diff --git a/meta/recipes-devtools/python/python/python-2.7.3-CVE-2013-1752-ftplib-fix.patch b/meta/recipes-devtools/python/python/python-2.7.3-CVE-2013-1752-ftplib-fix.patch new file mode 100644 index 0000000000..97c890e777 --- /dev/null +++ b/meta/recipes-devtools/python/python/python-2.7.3-CVE-2013-1752-ftplib-fix.patch @@ -0,0 +1,149 @@ + +# HG changeset patch +# User Serhiy Storchaka +# Date 1382277427 -10800 +# Node ID 44ac81e6d584758ee56a865a7c18d82505be0643 +# Parent 625ece68d79a27d376889579c414ed4b2d8a2649 +Issue #16038: CVE-2013-1752: ftplib: Limit amount of data read by +limiting the call to readline(). Original patch by Michał +Jastrzębski and Giampaolo Rodola. + +diff --git a/Lib/ftplib.py b/Lib/ftplib.py +--- a/Lib/ftplib.py ++++ b/Lib/ftplib.py +@@ -55,6 +55,8 @@ MSG_OOB = 0x1 + + # The standard FTP server control port + FTP_PORT = 21 ++# The sizehint parameter passed to readline() calls ++MAXLINE = 8192 + + + # Exception raised when an error or invalid response is received +@@ -101,6 +103,7 @@ class FTP: + debugging = 0 + host = '' + port = FTP_PORT ++ maxline = MAXLINE + sock = None + file = None + welcome = None +@@ -180,7 +183,9 @@ class FTP: + # Internal: return one line from the server, stripping CRLF. + # Raise EOFError if the connection is closed + def getline(self): +- line = self.file.readline() ++ line = self.file.readline(self.maxline + 1) ++ if len(line) > self.maxline: ++ raise Error("got more than %d bytes" % self.maxline) + if self.debugging > 1: + print '*get*', self.sanitize(line) + if not line: raise EOFError +@@ -432,7 +437,9 @@ class FTP: + conn = self.transfercmd(cmd) + fp = conn.makefile('rb') + while 1: +- line = fp.readline() ++ line = fp.readline(self.maxline + 1) ++ if len(line) > self.maxline: ++ raise Error("got more than %d bytes" % self.maxline) + if self.debugging > 2: print '*retr*', repr(line) + if not line: + break +@@ -485,7 +492,9 @@ class FTP: + self.voidcmd('TYPE A') + conn = self.transfercmd(cmd) + while 1: +- buf = fp.readline() ++ buf = fp.readline(self.maxline + 1) ++ if len(buf) > self.maxline: ++ raise Error("got more than %d bytes" % self.maxline) + if not buf: break + if buf[-2:] != CRLF: + if buf[-1] in CRLF: buf = buf[:-1] +@@ -710,7 +719,9 @@ else: + fp = conn.makefile('rb') + try: + while 1: +- line = fp.readline() ++ line = fp.readline(self.maxline + 1) ++ if len(line) > self.maxline: ++ raise Error("got more than %d bytes" % self.maxline) + if self.debugging > 2: print '*retr*', repr(line) + if not line: + break +@@ -748,7 +759,9 @@ else: + conn = self.transfercmd(cmd) + try: + while 1: +- buf = fp.readline() ++ buf = fp.readline(self.maxline + 1) ++ if len(buf) > self.maxline: ++ raise Error("got more than %d bytes" % self.maxline) + if not buf: break + if buf[-2:] != CRLF: + if buf[-1] in CRLF: buf = buf[:-1] +@@ -905,7 +918,9 @@ class Netrc: + fp = open(filename, "r") + in_macro = 0 + while 1: +- line = fp.readline() ++ line = fp.readline(self.maxline + 1) ++ if len(line) > self.maxline: ++ raise Error("got more than %d bytes" % self.maxline) + if not line: break + if in_macro and line.strip(): + macro_lines.append(line) +diff --git a/Lib/test/test_ftplib.py b/Lib/test/test_ftplib.py +--- a/Lib/test/test_ftplib.py ++++ b/Lib/test/test_ftplib.py +@@ -65,6 +65,7 @@ class DummyFTPHandler(asynchat.async_cha + self.last_received_data = '' + self.next_response = '' + self.rest = None ++ self.next_retr_data = RETR_DATA + self.push('220 welcome') + + def collect_incoming_data(self, data): +@@ -189,7 +190,7 @@ class DummyFTPHandler(asynchat.async_cha + offset = int(self.rest) + else: + offset = 0 +- self.dtp.push(RETR_DATA[offset:]) ++ self.dtp.push(self.next_retr_data[offset:]) + self.dtp.close_when_done() + self.rest = None + +@@ -203,6 +204,11 @@ class DummyFTPHandler(asynchat.async_cha + self.dtp.push(NLST_DATA) + self.dtp.close_when_done() + ++ def cmd_setlongretr(self, arg): ++ # For testing. Next RETR will return long line. ++ self.next_retr_data = 'x' * int(arg) ++ self.push('125 setlongretr ok') ++ + + class DummyFTPServer(asyncore.dispatcher, threading.Thread): + +@@ -558,6 +564,20 @@ class TestFTPClass(TestCase): + # IPv4 is in use, just make sure send_epsv has not been used + self.assertEqual(self.server.handler.last_received_cmd, 'pasv') + ++ def test_line_too_long(self): ++ self.assertRaises(ftplib.Error, self.client.sendcmd, ++ 'x' * self.client.maxline * 2) ++ ++ def test_retrlines_too_long(self): ++ self.client.sendcmd('SETLONGRETR %d' % (self.client.maxline * 2)) ++ received = [] ++ self.assertRaises(ftplib.Error, ++ self.client.retrlines, 'retr', received.append) ++ ++ def test_storlines_too_long(self): ++ f = StringIO.StringIO('x' * self.client.maxline * 2) ++ self.assertRaises(ftplib.Error, self.client.storlines, 'stor', f) ++ + + class TestIPv6Environment(TestCase): + diff --git a/meta/recipes-devtools/python/python/python-2.7.3-CVE-2013-1752-imaplib-fix.patch b/meta/recipes-devtools/python/python/python-2.7.3-CVE-2013-1752-imaplib-fix.patch new file mode 100644 index 0000000000..f4bd84d831 --- /dev/null +++ b/meta/recipes-devtools/python/python/python-2.7.3-CVE-2013-1752-imaplib-fix.patch @@ -0,0 +1,37 @@ +Upstream-Status: Backport + +CVE-2013-1752: Change use of readline in imaplib module to limit line length. Patch by Emil Lind. + +Signed-off-by: Tudor Florea + +diff -r ce583eb0bec2 Lib/imaplib.py +--- a/Lib/imaplib.py Thu Feb 21 20:17:54 2013 +0200 ++++ b/Lib/imaplib.py Tue Feb 26 22:36:52 2013 +0100 +@@ -35,6 +35,15 @@ + 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 @@ + + 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/meta/recipes-devtools/python/python/python-2.7.3-CVE-2013-1752-nntplib-fix.patch b/meta/recipes-devtools/python/python/python-2.7.3-CVE-2013-1752-nntplib-fix.patch new file mode 100644 index 0000000000..443e137ea5 --- /dev/null +++ b/meta/recipes-devtools/python/python/python-2.7.3-CVE-2013-1752-nntplib-fix.patch @@ -0,0 +1,105 @@ +Upstream-Status: Backport + +CVE-2013-1752: nntplib: Limit maximum line lengths to 2048 to prevent +readline() calls from consuming too much memory. +Patch by Jyrki Pulliainen. + +Signed-off-by: Tudor Florea + +diff -r 936621d33c38 Lib/nntplib.py +--- a/Lib/nntplib.py Wed Feb 20 18:19:55 2013 -0500 ++++ b/Lib/nntplib.py Mon Sep 30 23:42:09 2013 +0200 +@@ -37,6 +37,13 @@ + "error_reply","error_temp","error_perm","error_proto", + "error_data",] + ++# maximal line length when calling readline(). This is to prevent ++# reading arbitrary length lines. RFC 3977 limits NNTP line length to ++# 512 characters, including CRLF. We have selected 2048 just to be on ++# the safe side. ++_MAXLINE = 2048 ++ ++ + # Exceptions raised when an error or invalid response is received + class NNTPError(Exception): + """Base class for all nntplib exceptions""" +@@ -200,7 +207,9 @@ + def getline(self): + """Internal: return one line from the server, stripping CRLF. + Raise EOFError if the connection is closed.""" +- line = self.file.readline() ++ line = self.file.readline(_MAXLINE + 1) ++ if len(line) > _MAXLINE: ++ raise NNTPProtocolError('line too long') + if self.debugging > 1: + print '*get*', repr(line) + if not line: raise EOFError +diff -r 936621d33c38 Lib/test/test_nntplib.py +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/Lib/test/test_nntplib.py Mon Sep 30 23:42:09 2013 +0200 +@@ -0,0 +1,65 @@ ++import socket ++import threading ++import nntplib ++import time ++ ++from unittest import TestCase ++from test import test_support ++ ++HOST = test_support.HOST ++ ++ ++def server(evt, serv, evil=False): ++ serv.listen(5) ++ try: ++ conn, addr = serv.accept() ++ except socket.timeout: ++ pass ++ else: ++ if evil: ++ conn.send("1 I'm too long response" * 3000 + "\n") ++ else: ++ conn.send("1 I'm OK response\n") ++ conn.close() ++ finally: ++ serv.close() ++ evt.set() ++ ++ ++class BaseServerTest(TestCase): ++ def setUp(self): ++ self.evt = threading.Event() ++ self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ++ self.sock.settimeout(3) ++ self.port = test_support.bind_port(self.sock) ++ threading.Thread( ++ target=server, ++ args=(self.evt, self.sock, self.evil)).start() ++ time.sleep(.1) ++ ++ def tearDown(self): ++ self.evt.wait() ++ ++ ++class ServerTests(BaseServerTest): ++ evil = False ++ ++ def test_basic_connect(self): ++ nntp = nntplib.NNTP('localhost', self.port) ++ nntp.sock.close() ++ ++ ++class EvilServerTests(BaseServerTest): ++ evil = True ++ ++ def test_too_long_line(self): ++ self.assertRaises(nntplib.NNTPProtocolError, ++ nntplib.NNTP, 'localhost', self.port) ++ ++ ++def test_main(verbose=None): ++ test_support.run_unittest(EvilServerTests) ++ test_support.run_unittest(ServerTests) ++ ++if __name__ == '__main__': ++ test_main() diff --git a/meta/recipes-devtools/python/python/python-2.7.3-CVE-2013-1752-poplib-fix.patch b/meta/recipes-devtools/python/python/python-2.7.3-CVE-2013-1752-poplib-fix.patch new file mode 100644 index 0000000000..15a5a2c63c --- /dev/null +++ b/meta/recipes-devtools/python/python/python-2.7.3-CVE-2013-1752-poplib-fix.patch @@ -0,0 +1,90 @@ +diff --git a/Lib/poplib.py b/Lib/poplib.py +--- a/Lib/poplib.py ++++ b/Lib/poplib.py +@@ -27,16 +27,22 @@ POP3_PORT = 110 + # POP SSL PORT + POP3_SSL_PORT = 995 + + # Line terminators (we always output CRLF, but accept any of CRLF, LFCR, LF) + CR = '\r' + LF = '\n' + CRLF = CR+LF + ++# maximal line length when calling readline(). This is to prevent ++# reading arbitrary length 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: + + """This class supports both the minimal and optional command sets. + Arguments can be strings or integers (where appropriate) + (e.g.: retr(1) and retr('1') both work equally well. + + Minimal Command Set: +@@ -98,17 +104,19 @@ class POP3: + self._putline(line) + + + # Internal: return one line from the server, stripping CRLF. + # This is where all the CPU time of this module is consumed. + # 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) + # server can send any combination of CR & LF + # however, 'readline()' returns lines ending in LF + # so only possibilities are ...LF, ...CRLF, CR...LF + if line[-2:] == CRLF: + return line[:-2], octets +@@ -360,16 +368,18 @@ else: + self.buffer += localbuf + + def _getline(self): + line = "" + renewline = re.compile(r'.*?\n') + match = renewline.match(self.buffer) + while not match: + self._fillBuffer() ++ if len(self.buffer) > _MAXLINE: ++ raise error_proto('line too long') + match = renewline.match(self.buffer) + line = match.group(0) + self.buffer = renewline.sub('' ,self.buffer, 1) + if self._debugging > 1: print '*get*', repr(line) + + octets = len(line) + if line[-2:] == CRLF: + return line[:-2], octets +diff --git a/Lib/test/test_poplib.py b/Lib/test/test_poplib.py +--- a/Lib/test/test_poplib.py ++++ b/Lib/test/test_poplib.py +@@ -193,16 +193,20 @@ class TestPOP3Class(TestCase): + def test_retr(self): + expected = ('+OK 116 bytes', + ['From: postmaster@python.org', 'Content-Type: text/plain', + 'MIME-Version: 1.0', 'Subject: Dummy', + '', 'line1', 'line2', 'line3'], + 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')) + + def test_noop(self): + self.assertOK(self.client.noop()) + + def test_rpop(self): + self.assertOK(self.client.rpop('foo')) diff --git a/meta/recipes-devtools/python/python_2.7.3.bb b/meta/recipes-devtools/python/python_2.7.3.bb index 5e0181da56..42c43156b9 100644 --- a/meta/recipes-devtools/python/python_2.7.3.bb +++ b/meta/recipes-devtools/python/python_2.7.3.bb @@ -43,6 +43,10 @@ SRC_URI += "\ file://ctypes-libffi-aarch64.patch \ file://libffi-aarch64.patch \ file://python-2.7.3-CVE-2013-1752-httplib-fix.patch \ + file://python-2.7.3-CVE-2013-1752-ftplib-fix.patch \ + file://python-2.7.3-CVE-2013-1752-imaplib-fix.patch \ + file://python-2.7.3-CVE-2013-1752-nntplib-fix.patch \ + file://python-2.7.3-CVE-2013-1752-poplib-fix.patch \ " S = "${WORKDIR}/Python-${PV}" -- cgit v1.2.3-54-g00ecf