diff options
| author | Tudor Florea <tudor.florea@enea.com> | 2015-09-22 01:38:33 +0200 |
|---|---|---|
| committer | Tudor Florea <tudor.florea@enea.com> | 2015-10-22 05:43:46 +0200 |
| commit | 15f68138d4d0ff56704217369facc8baf03783a5 (patch) | |
| tree | a19ad8e95a548fead58157cea6204b5eb6815237 | |
| parent | ff46766bf74cb96e103715de232c3cf09a69616e (diff) | |
| download | poky-15f68138d4d0ff56704217369facc8baf03783a5.tar.gz | |
python: Backport CVE-2013-1752 fix from upstream
Signed-off-by: Tudor Florea <tudor.florea@enea.com>
5 files changed, 385 insertions, 0 deletions
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 @@ | |||
| 1 | |||
| 2 | # HG changeset patch | ||
| 3 | # User Serhiy Storchaka <storchaka@gmail.com> | ||
| 4 | # Date 1382277427 -10800 | ||
| 5 | # Node ID 44ac81e6d584758ee56a865a7c18d82505be0643 | ||
| 6 | # Parent 625ece68d79a27d376889579c414ed4b2d8a2649 | ||
| 7 | Issue #16038: CVE-2013-1752: ftplib: Limit amount of data read by | ||
| 8 | limiting the call to readline(). Original patch by Michał | ||
| 9 | Jastrzębski and Giampaolo Rodola. | ||
| 10 | |||
| 11 | diff --git a/Lib/ftplib.py b/Lib/ftplib.py | ||
| 12 | --- a/Lib/ftplib.py | ||
| 13 | +++ b/Lib/ftplib.py | ||
| 14 | @@ -55,6 +55,8 @@ MSG_OOB = 0x1 | ||
| 15 | |||
| 16 | # The standard FTP server control port | ||
| 17 | FTP_PORT = 21 | ||
| 18 | +# The sizehint parameter passed to readline() calls | ||
| 19 | +MAXLINE = 8192 | ||
| 20 | |||
| 21 | |||
| 22 | # Exception raised when an error or invalid response is received | ||
| 23 | @@ -101,6 +103,7 @@ class FTP: | ||
| 24 | debugging = 0 | ||
| 25 | host = '' | ||
| 26 | port = FTP_PORT | ||
| 27 | + maxline = MAXLINE | ||
| 28 | sock = None | ||
| 29 | file = None | ||
| 30 | welcome = None | ||
| 31 | @@ -180,7 +183,9 @@ class FTP: | ||
| 32 | # Internal: return one line from the server, stripping CRLF. | ||
| 33 | # Raise EOFError if the connection is closed | ||
| 34 | def getline(self): | ||
| 35 | - line = self.file.readline() | ||
| 36 | + line = self.file.readline(self.maxline + 1) | ||
| 37 | + if len(line) > self.maxline: | ||
| 38 | + raise Error("got more than %d bytes" % self.maxline) | ||
| 39 | if self.debugging > 1: | ||
| 40 | print '*get*', self.sanitize(line) | ||
| 41 | if not line: raise EOFError | ||
| 42 | @@ -432,7 +437,9 @@ class FTP: | ||
| 43 | conn = self.transfercmd(cmd) | ||
| 44 | fp = conn.makefile('rb') | ||
| 45 | while 1: | ||
| 46 | - line = fp.readline() | ||
| 47 | + line = fp.readline(self.maxline + 1) | ||
| 48 | + if len(line) > self.maxline: | ||
| 49 | + raise Error("got more than %d bytes" % self.maxline) | ||
| 50 | if self.debugging > 2: print '*retr*', repr(line) | ||
| 51 | if not line: | ||
| 52 | break | ||
| 53 | @@ -485,7 +492,9 @@ class FTP: | ||
| 54 | self.voidcmd('TYPE A') | ||
| 55 | conn = self.transfercmd(cmd) | ||
| 56 | while 1: | ||
| 57 | - buf = fp.readline() | ||
| 58 | + buf = fp.readline(self.maxline + 1) | ||
| 59 | + if len(buf) > self.maxline: | ||
| 60 | + raise Error("got more than %d bytes" % self.maxline) | ||
| 61 | if not buf: break | ||
| 62 | if buf[-2:] != CRLF: | ||
| 63 | if buf[-1] in CRLF: buf = buf[:-1] | ||
| 64 | @@ -710,7 +719,9 @@ else: | ||
| 65 | fp = conn.makefile('rb') | ||
| 66 | try: | ||
| 67 | while 1: | ||
| 68 | - line = fp.readline() | ||
| 69 | + line = fp.readline(self.maxline + 1) | ||
| 70 | + if len(line) > self.maxline: | ||
| 71 | + raise Error("got more than %d bytes" % self.maxline) | ||
| 72 | if self.debugging > 2: print '*retr*', repr(line) | ||
| 73 | if not line: | ||
| 74 | break | ||
| 75 | @@ -748,7 +759,9 @@ else: | ||
| 76 | conn = self.transfercmd(cmd) | ||
| 77 | try: | ||
| 78 | while 1: | ||
| 79 | - buf = fp.readline() | ||
| 80 | + buf = fp.readline(self.maxline + 1) | ||
| 81 | + if len(buf) > self.maxline: | ||
| 82 | + raise Error("got more than %d bytes" % self.maxline) | ||
| 83 | if not buf: break | ||
| 84 | if buf[-2:] != CRLF: | ||
| 85 | if buf[-1] in CRLF: buf = buf[:-1] | ||
| 86 | @@ -905,7 +918,9 @@ class Netrc: | ||
| 87 | fp = open(filename, "r") | ||
| 88 | in_macro = 0 | ||
| 89 | while 1: | ||
| 90 | - line = fp.readline() | ||
| 91 | + line = fp.readline(self.maxline + 1) | ||
| 92 | + if len(line) > self.maxline: | ||
| 93 | + raise Error("got more than %d bytes" % self.maxline) | ||
| 94 | if not line: break | ||
| 95 | if in_macro and line.strip(): | ||
| 96 | macro_lines.append(line) | ||
| 97 | diff --git a/Lib/test/test_ftplib.py b/Lib/test/test_ftplib.py | ||
| 98 | --- a/Lib/test/test_ftplib.py | ||
| 99 | +++ b/Lib/test/test_ftplib.py | ||
| 100 | @@ -65,6 +65,7 @@ class DummyFTPHandler(asynchat.async_cha | ||
| 101 | self.last_received_data = '' | ||
| 102 | self.next_response = '' | ||
| 103 | self.rest = None | ||
| 104 | + self.next_retr_data = RETR_DATA | ||
| 105 | self.push('220 welcome') | ||
| 106 | |||
| 107 | def collect_incoming_data(self, data): | ||
| 108 | @@ -189,7 +190,7 @@ class DummyFTPHandler(asynchat.async_cha | ||
| 109 | offset = int(self.rest) | ||
| 110 | else: | ||
| 111 | offset = 0 | ||
| 112 | - self.dtp.push(RETR_DATA[offset:]) | ||
| 113 | + self.dtp.push(self.next_retr_data[offset:]) | ||
| 114 | self.dtp.close_when_done() | ||
| 115 | self.rest = None | ||
| 116 | |||
| 117 | @@ -203,6 +204,11 @@ class DummyFTPHandler(asynchat.async_cha | ||
| 118 | self.dtp.push(NLST_DATA) | ||
| 119 | self.dtp.close_when_done() | ||
| 120 | |||
| 121 | + def cmd_setlongretr(self, arg): | ||
| 122 | + # For testing. Next RETR will return long line. | ||
| 123 | + self.next_retr_data = 'x' * int(arg) | ||
| 124 | + self.push('125 setlongretr ok') | ||
| 125 | + | ||
| 126 | |||
| 127 | class DummyFTPServer(asyncore.dispatcher, threading.Thread): | ||
| 128 | |||
| 129 | @@ -558,6 +564,20 @@ class TestFTPClass(TestCase): | ||
| 130 | # IPv4 is in use, just make sure send_epsv has not been used | ||
| 131 | self.assertEqual(self.server.handler.last_received_cmd, 'pasv') | ||
| 132 | |||
| 133 | + def test_line_too_long(self): | ||
| 134 | + self.assertRaises(ftplib.Error, self.client.sendcmd, | ||
| 135 | + 'x' * self.client.maxline * 2) | ||
| 136 | + | ||
| 137 | + def test_retrlines_too_long(self): | ||
| 138 | + self.client.sendcmd('SETLONGRETR %d' % (self.client.maxline * 2)) | ||
| 139 | + received = [] | ||
| 140 | + self.assertRaises(ftplib.Error, | ||
| 141 | + self.client.retrlines, 'retr', received.append) | ||
| 142 | + | ||
| 143 | + def test_storlines_too_long(self): | ||
| 144 | + f = StringIO.StringIO('x' * self.client.maxline * 2) | ||
| 145 | + self.assertRaises(ftplib.Error, self.client.storlines, 'stor', f) | ||
| 146 | + | ||
| 147 | |||
| 148 | class TestIPv6Environment(TestCase): | ||
| 149 | |||
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 @@ | |||
| 1 | Upstream-Status: Backport | ||
| 2 | |||
| 3 | CVE-2013-1752: Change use of readline in imaplib module to limit line length. Patch by Emil Lind. | ||
| 4 | |||
| 5 | Signed-off-by: Tudor Florea <tudor.florea@enea.com> | ||
| 6 | |||
| 7 | diff -r ce583eb0bec2 Lib/imaplib.py | ||
| 8 | --- a/Lib/imaplib.py Thu Feb 21 20:17:54 2013 +0200 | ||
| 9 | +++ b/Lib/imaplib.py Tue Feb 26 22:36:52 2013 +0100 | ||
| 10 | @@ -35,6 +35,15 @@ | ||
| 11 | IMAP4_SSL_PORT = 993 | ||
| 12 | AllowedVersions = ('IMAP4REV1', 'IMAP4') # Most recent first | ||
| 13 | |||
| 14 | +# Maximal line length when calling readline(). This is to prevent | ||
| 15 | +# reading arbitrary length lines. RFC 3501 and 2060 (IMAP 4rev1) | ||
| 16 | +# don't specify a line length. RFC 2683 however suggests limiting client | ||
| 17 | +# command lines to 1000 octets and server command lines to 8000 octets. | ||
| 18 | +# We have selected 10000 for some extra margin and since that is supposedly | ||
| 19 | +# also what UW and Panda IMAP does. | ||
| 20 | +_MAXLINE = 10000 | ||
| 21 | + | ||
| 22 | + | ||
| 23 | # Commands | ||
| 24 | |||
| 25 | Commands = { | ||
| 26 | @@ -237,7 +246,10 @@ | ||
| 27 | |||
| 28 | def readline(self): | ||
| 29 | """Read line from remote.""" | ||
| 30 | - return self.file.readline() | ||
| 31 | + line = self.file.readline(_MAXLINE + 1) | ||
| 32 | + if len(line) > _MAXLINE: | ||
| 33 | + raise self.error("got more than %d bytes" % _MAXLINE) | ||
| 34 | + return line | ||
| 35 | |||
| 36 | |||
| 37 | 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 @@ | |||
| 1 | Upstream-Status: Backport | ||
| 2 | |||
| 3 | CVE-2013-1752: nntplib: Limit maximum line lengths to 2048 to prevent | ||
| 4 | readline() calls from consuming too much memory. | ||
| 5 | Patch by Jyrki Pulliainen. | ||
| 6 | |||
| 7 | Signed-off-by: Tudor Florea <tudor.florea@enea.com> | ||
| 8 | |||
| 9 | diff -r 936621d33c38 Lib/nntplib.py | ||
| 10 | --- a/Lib/nntplib.py Wed Feb 20 18:19:55 2013 -0500 | ||
| 11 | +++ b/Lib/nntplib.py Mon Sep 30 23:42:09 2013 +0200 | ||
| 12 | @@ -37,6 +37,13 @@ | ||
| 13 | "error_reply","error_temp","error_perm","error_proto", | ||
| 14 | "error_data",] | ||
| 15 | |||
| 16 | +# maximal line length when calling readline(). This is to prevent | ||
| 17 | +# reading arbitrary length lines. RFC 3977 limits NNTP line length to | ||
| 18 | +# 512 characters, including CRLF. We have selected 2048 just to be on | ||
| 19 | +# the safe side. | ||
| 20 | +_MAXLINE = 2048 | ||
| 21 | + | ||
| 22 | + | ||
| 23 | # Exceptions raised when an error or invalid response is received | ||
| 24 | class NNTPError(Exception): | ||
| 25 | """Base class for all nntplib exceptions""" | ||
| 26 | @@ -200,7 +207,9 @@ | ||
| 27 | def getline(self): | ||
| 28 | """Internal: return one line from the server, stripping CRLF. | ||
| 29 | Raise EOFError if the connection is closed.""" | ||
| 30 | - line = self.file.readline() | ||
| 31 | + line = self.file.readline(_MAXLINE + 1) | ||
| 32 | + if len(line) > _MAXLINE: | ||
| 33 | + raise NNTPProtocolError('line too long') | ||
| 34 | if self.debugging > 1: | ||
| 35 | print '*get*', repr(line) | ||
| 36 | if not line: raise EOFError | ||
| 37 | diff -r 936621d33c38 Lib/test/test_nntplib.py | ||
| 38 | --- /dev/null Thu Jan 01 00:00:00 1970 +0000 | ||
| 39 | +++ b/Lib/test/test_nntplib.py Mon Sep 30 23:42:09 2013 +0200 | ||
| 40 | @@ -0,0 +1,65 @@ | ||
| 41 | +import socket | ||
| 42 | +import threading | ||
| 43 | +import nntplib | ||
| 44 | +import time | ||
| 45 | + | ||
| 46 | +from unittest import TestCase | ||
| 47 | +from test import test_support | ||
| 48 | + | ||
| 49 | +HOST = test_support.HOST | ||
| 50 | + | ||
| 51 | + | ||
| 52 | +def server(evt, serv, evil=False): | ||
| 53 | + serv.listen(5) | ||
| 54 | + try: | ||
| 55 | + conn, addr = serv.accept() | ||
| 56 | + except socket.timeout: | ||
| 57 | + pass | ||
| 58 | + else: | ||
| 59 | + if evil: | ||
| 60 | + conn.send("1 I'm too long response" * 3000 + "\n") | ||
| 61 | + else: | ||
| 62 | + conn.send("1 I'm OK response\n") | ||
| 63 | + conn.close() | ||
| 64 | + finally: | ||
| 65 | + serv.close() | ||
| 66 | + evt.set() | ||
| 67 | + | ||
| 68 | + | ||
| 69 | +class BaseServerTest(TestCase): | ||
| 70 | + def setUp(self): | ||
| 71 | + self.evt = threading.Event() | ||
| 72 | + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||
| 73 | + self.sock.settimeout(3) | ||
| 74 | + self.port = test_support.bind_port(self.sock) | ||
| 75 | + threading.Thread( | ||
| 76 | + target=server, | ||
| 77 | + args=(self.evt, self.sock, self.evil)).start() | ||
| 78 | + time.sleep(.1) | ||
| 79 | + | ||
| 80 | + def tearDown(self): | ||
| 81 | + self.evt.wait() | ||
| 82 | + | ||
| 83 | + | ||
| 84 | +class ServerTests(BaseServerTest): | ||
| 85 | + evil = False | ||
| 86 | + | ||
| 87 | + def test_basic_connect(self): | ||
| 88 | + nntp = nntplib.NNTP('localhost', self.port) | ||
| 89 | + nntp.sock.close() | ||
| 90 | + | ||
| 91 | + | ||
| 92 | +class EvilServerTests(BaseServerTest): | ||
| 93 | + evil = True | ||
| 94 | + | ||
| 95 | + def test_too_long_line(self): | ||
| 96 | + self.assertRaises(nntplib.NNTPProtocolError, | ||
| 97 | + nntplib.NNTP, 'localhost', self.port) | ||
| 98 | + | ||
| 99 | + | ||
| 100 | +def test_main(verbose=None): | ||
| 101 | + test_support.run_unittest(EvilServerTests) | ||
| 102 | + test_support.run_unittest(ServerTests) | ||
| 103 | + | ||
| 104 | +if __name__ == '__main__': | ||
| 105 | + 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 @@ | |||
| 1 | diff --git a/Lib/poplib.py b/Lib/poplib.py | ||
| 2 | --- a/Lib/poplib.py | ||
| 3 | +++ b/Lib/poplib.py | ||
| 4 | @@ -27,16 +27,22 @@ POP3_PORT = 110 | ||
| 5 | # POP SSL PORT | ||
| 6 | POP3_SSL_PORT = 995 | ||
| 7 | |||
| 8 | # Line terminators (we always output CRLF, but accept any of CRLF, LFCR, LF) | ||
| 9 | CR = '\r' | ||
| 10 | LF = '\n' | ||
| 11 | CRLF = CR+LF | ||
| 12 | |||
| 13 | +# maximal line length when calling readline(). This is to prevent | ||
| 14 | +# reading arbitrary length lines. RFC 1939 limits POP3 line length to | ||
| 15 | +# 512 characters, including CRLF. We have selected 2048 just to be on | ||
| 16 | +# the safe side. | ||
| 17 | +_MAXLINE = 2048 | ||
| 18 | + | ||
| 19 | |||
| 20 | class POP3: | ||
| 21 | |||
| 22 | """This class supports both the minimal and optional command sets. | ||
| 23 | Arguments can be strings or integers (where appropriate) | ||
| 24 | (e.g.: retr(1) and retr('1') both work equally well. | ||
| 25 | |||
| 26 | Minimal Command Set: | ||
| 27 | @@ -98,17 +104,19 @@ class POP3: | ||
| 28 | self._putline(line) | ||
| 29 | |||
| 30 | |||
| 31 | # Internal: return one line from the server, stripping CRLF. | ||
| 32 | # This is where all the CPU time of this module is consumed. | ||
| 33 | # Raise error_proto('-ERR EOF') if the connection is closed. | ||
| 34 | |||
| 35 | def _getline(self): | ||
| 36 | - line = self.file.readline() | ||
| 37 | + line = self.file.readline(_MAXLINE + 1) | ||
| 38 | + if len(line) > _MAXLINE: | ||
| 39 | + raise error_proto('line too long') | ||
| 40 | if self._debugging > 1: print '*get*', repr(line) | ||
| 41 | if not line: raise error_proto('-ERR EOF') | ||
| 42 | octets = len(line) | ||
| 43 | # server can send any combination of CR & LF | ||
| 44 | # however, 'readline()' returns lines ending in LF | ||
| 45 | # so only possibilities are ...LF, ...CRLF, CR...LF | ||
| 46 | if line[-2:] == CRLF: | ||
| 47 | return line[:-2], octets | ||
| 48 | @@ -360,16 +368,18 @@ else: | ||
| 49 | self.buffer += localbuf | ||
| 50 | |||
| 51 | def _getline(self): | ||
| 52 | line = "" | ||
| 53 | renewline = re.compile(r'.*?\n') | ||
| 54 | match = renewline.match(self.buffer) | ||
| 55 | while not match: | ||
| 56 | self._fillBuffer() | ||
| 57 | + if len(self.buffer) > _MAXLINE: | ||
| 58 | + raise error_proto('line too long') | ||
| 59 | match = renewline.match(self.buffer) | ||
| 60 | line = match.group(0) | ||
| 61 | self.buffer = renewline.sub('' ,self.buffer, 1) | ||
| 62 | if self._debugging > 1: print '*get*', repr(line) | ||
| 63 | |||
| 64 | octets = len(line) | ||
| 65 | if line[-2:] == CRLF: | ||
| 66 | return line[:-2], octets | ||
| 67 | diff --git a/Lib/test/test_poplib.py b/Lib/test/test_poplib.py | ||
| 68 | --- a/Lib/test/test_poplib.py | ||
| 69 | +++ b/Lib/test/test_poplib.py | ||
| 70 | @@ -193,16 +193,20 @@ class TestPOP3Class(TestCase): | ||
| 71 | def test_retr(self): | ||
| 72 | expected = ('+OK 116 bytes', | ||
| 73 | ['From: postmaster@python.org', 'Content-Type: text/plain', | ||
| 74 | 'MIME-Version: 1.0', 'Subject: Dummy', | ||
| 75 | '', 'line1', 'line2', 'line3'], | ||
| 76 | 113) | ||
| 77 | self.assertEqual(self.client.retr('foo'), expected) | ||
| 78 | |||
| 79 | + def test_too_long_lines(self): | ||
| 80 | + self.assertRaises(poplib.error_proto, self.client._shortcmd, | ||
| 81 | + 'echo +%s' % ((poplib._MAXLINE + 10) * 'a')) | ||
| 82 | + | ||
| 83 | def test_dele(self): | ||
| 84 | self.assertOK(self.client.dele('foo')) | ||
| 85 | |||
| 86 | def test_noop(self): | ||
| 87 | self.assertOK(self.client.noop()) | ||
| 88 | |||
| 89 | def test_rpop(self): | ||
| 90 | 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 += "\ | |||
| 43 | file://ctypes-libffi-aarch64.patch \ | 43 | file://ctypes-libffi-aarch64.patch \ |
| 44 | file://libffi-aarch64.patch \ | 44 | file://libffi-aarch64.patch \ |
| 45 | file://python-2.7.3-CVE-2013-1752-httplib-fix.patch \ | 45 | file://python-2.7.3-CVE-2013-1752-httplib-fix.patch \ |
| 46 | file://python-2.7.3-CVE-2013-1752-ftplib-fix.patch \ | ||
| 47 | file://python-2.7.3-CVE-2013-1752-imaplib-fix.patch \ | ||
| 48 | file://python-2.7.3-CVE-2013-1752-nntplib-fix.patch \ | ||
| 49 | file://python-2.7.3-CVE-2013-1752-poplib-fix.patch \ | ||
| 46 | " | 50 | " |
| 47 | 51 | ||
| 48 | S = "${WORKDIR}/Python-${PV}" | 52 | S = "${WORKDIR}/Python-${PV}" |
