| |
| # HG changeset patch |
| # User Barry Warsaw <barry@python.org> |
| # Date 1380582569 14400 |
| # Node ID 36680a7c0e22686df9c338a9ca3cdb2c60e05b27 |
| # Parent 0f5611bca5a284c0b5f978e83a05818f0907bda8# Parent 731abf7834c43efb321231e65e7dd76ad9e8e661 |
| - Issue #16040: CVE-2013-1752: nntplib: Limit maximum line lengths to 2048 to |
| prevent readline() calls from consuming too much memory. Patch by Jyrki |
| Pulliainen. |
| |
| diff --git a/Lib/nntplib.py b/Lib/nntplib.py |
| |
| |
| @@ -37,6 +37,13 @@ import socket |
| "error_reply","error_temp","error_perm","error_proto", |
| "error_data",] |
| |
| +# maximal line length when calling readline(). This is to prevent |
| +# reading arbitrary lenght 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 @@ class NNTP: |
| 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 NNTPDataError('line too long') |
| if self.debugging > 1: |
| print '*get*', repr(line) |
| if not line: raise EOFError |
| diff --git a/Lib/test/test_nntplib.py b/Lib/test/test_nntplib.py |
| new file mode 100644 |
| |
| |
| @@ -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.NNTPDataError, |
| + 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() |