Blame SOURCES/00203-CVE-2013-1752-nntplib.patch

925e6b
925e6b
# HG changeset patch
925e6b
# User Barry Warsaw <barry@python.org>
925e6b
# Date 1380582569 14400
925e6b
# Node ID 36680a7c0e22686df9c338a9ca3cdb2c60e05b27
925e6b
# Parent  0f5611bca5a284c0b5f978e83a05818f0907bda8# Parent  731abf7834c43efb321231e65e7dd76ad9e8e661
925e6b
- Issue #16040: CVE-2013-1752: nntplib: Limit maximum line lengths to 2048 to
925e6b
  prevent readline() calls from consuming too much memory.  Patch by Jyrki
925e6b
  Pulliainen.
925e6b
925e6b
diff --git a/Lib/nntplib.py b/Lib/nntplib.py
925e6b
--- a/Lib/nntplib.py
925e6b
+++ b/Lib/nntplib.py
925e6b
@@ -37,6 +37,13 @@ import socket
925e6b
            "error_reply","error_temp","error_perm","error_proto",
925e6b
            "error_data",]
925e6b
 
925e6b
+# maximal line length when calling readline(). This is to prevent
925e6b
+# reading arbitrary lenght lines. RFC 3977 limits NNTP line length to
925e6b
+# 512 characters, including CRLF. We have selected 2048 just to be on
925e6b
+# the safe side.
925e6b
+_MAXLINE = 2048
925e6b
+
925e6b
+
925e6b
 # Exceptions raised when an error or invalid response is received
925e6b
 class NNTPError(Exception):
925e6b
     """Base class for all nntplib exceptions"""
925e6b
@@ -200,7 +207,9 @@ class NNTP:
925e6b
     def getline(self):
925e6b
         """Internal: return one line from the server, stripping CRLF.
925e6b
         Raise EOFError if the connection is closed."""
925e6b
-        line = self.file.readline()
925e6b
+        line = self.file.readline(_MAXLINE + 1)
925e6b
+        if len(line) > _MAXLINE:
925e6b
+            raise NNTPDataError('line too long')
925e6b
         if self.debugging > 1:
925e6b
             print '*get*', repr(line)
925e6b
         if not line: raise EOFError
925e6b
diff --git a/Lib/test/test_nntplib.py b/Lib/test/test_nntplib.py
925e6b
new file mode 100644
925e6b
--- /dev/null
925e6b
+++ b/Lib/test/test_nntplib.py
925e6b
@@ -0,0 +1,65 @@
925e6b
+import socket
925e6b
+import threading
925e6b
+import nntplib
925e6b
+import time
925e6b
+
925e6b
+from unittest import TestCase
925e6b
+from test import test_support
925e6b
+
925e6b
+HOST = test_support.HOST
925e6b
+
925e6b
+
925e6b
+def server(evt, serv, evil=False):
925e6b
+    serv.listen(5)
925e6b
+    try:
925e6b
+        conn, addr = serv.accept()
925e6b
+    except socket.timeout:
925e6b
+        pass
925e6b
+    else:
925e6b
+        if evil:
925e6b
+            conn.send("1 I'm too long response" * 3000 + "\n")
925e6b
+        else:
925e6b
+            conn.send("1 I'm OK response\n")
925e6b
+        conn.close()
925e6b
+    finally:
925e6b
+        serv.close()
925e6b
+        evt.set()
925e6b
+
925e6b
+
925e6b
+class BaseServerTest(TestCase):
925e6b
+    def setUp(self):
925e6b
+        self.evt = threading.Event()
925e6b
+        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
925e6b
+        self.sock.settimeout(3)
925e6b
+        self.port = test_support.bind_port(self.sock)
925e6b
+        threading.Thread(
925e6b
+            target=server,
925e6b
+            args=(self.evt, self.sock, self.evil)).start()
925e6b
+        time.sleep(.1)
925e6b
+
925e6b
+    def tearDown(self):
925e6b
+        self.evt.wait()
925e6b
+
925e6b
+
925e6b
+class ServerTests(BaseServerTest):
925e6b
+    evil = False
925e6b
+
925e6b
+    def test_basic_connect(self):
925e6b
+        nntp = nntplib.NNTP('localhost', self.port)
925e6b
+        nntp.sock.close()
925e6b
+
925e6b
+
925e6b
+class EvilServerTests(BaseServerTest):
925e6b
+    evil = True
925e6b
+
925e6b
+    def test_too_long_line(self):
925e6b
+        self.assertRaises(nntplib.NNTPDataError,
925e6b
+                          nntplib.NNTP, 'localhost', self.port)
925e6b
+
925e6b
+
925e6b
+def test_main(verbose=None):
925e6b
+    test_support.run_unittest(EvilServerTests)
925e6b
+    test_support.run_unittest(ServerTests)
925e6b
+
925e6b
+if __name__ == '__main__':
925e6b
+    test_main()