[Python-checkins] r86955 - in python/branches/py3k: Lib/smtpd.py Lib/test/test_smtpd.py Misc/NEWS

georg.brandl python-checkins at python.org
Fri Dec 3 08:38:23 CET 2010


Author: georg.brandl
Date: Fri Dec  3 08:38:22 2010
New Revision: 86955

Log:
#1745035: add limits for command and data size to smtpd; patch by Savio Sena.

Modified:
   python/branches/py3k/Lib/smtpd.py
   python/branches/py3k/Lib/test/test_smtpd.py
   python/branches/py3k/Misc/NEWS

Modified: python/branches/py3k/Lib/smtpd.py
==============================================================================
--- python/branches/py3k/Lib/smtpd.py	(original)
+++ python/branches/py3k/Lib/smtpd.py	Fri Dec  3 08:38:22 2010
@@ -109,6 +109,9 @@
     COMMAND = 0
     DATA = 1
 
+    data_size_limit = 33554432
+    command_size_limit = 512
+
     def __init__(self, server, conn, addr):
         asynchat.async_chat.__init__(self, conn)
         self.smtp_server = server
@@ -121,6 +124,7 @@
         self.rcpttos = []
         self.received_data = ''
         self.fqdn = socket.getfqdn()
+        self.num_bytes = 0
         try:
             self.peer = conn.getpeername()
         except socket.error as err:
@@ -262,6 +266,15 @@
 
     # Implementation of base class abstract method
     def collect_incoming_data(self, data):
+        limit = None
+        if self.smtp_state == self.COMMAND:
+            limit = self.command_size_limit
+        elif self.smtp_state == self.DATA:
+            limit = self.data_size_limit
+        if limit and self.num_bytes > limit:
+            return
+        elif limit:
+            self.num_bytes += len(data)
         self.received_lines.append(str(data, "utf8"))
 
     # Implementation of base class abstract method
@@ -270,6 +283,11 @@
         print('Data:', repr(line), file=DEBUGSTREAM)
         self.received_lines = []
         if self.smtp_state == self.COMMAND:
+            if self.num_bytes > self.command_size_limit:
+                self.push('500 Error: line too long')
+                self.num_bytes = 0
+                return
+            self.num_bytes = 0
             if not line:
                 self.push('500 Error: bad syntax')
                 return
@@ -290,6 +308,11 @@
         else:
             if self.smtp_state != self.DATA:
                 self.push('451 Internal confusion')
+                self.num_bytes = 0
+                return
+            if self.num_bytes > self.data_size_limit:
+                self.push('552 Error: Too much mail data')
+                self.num_bytes = 0
                 return
             # Remove extraneous carriage returns and de-transparency according
             # to RFC 821, Section 4.5.2.
@@ -307,6 +330,7 @@
             self.rcpttos = []
             self.mailfrom = None
             self.smtp_state = self.COMMAND
+            self.num_bytes = 0
             self.set_terminator(b'\r\n')
             if not status:
                 self.push('250 Ok')

Modified: python/branches/py3k/Lib/test/test_smtpd.py
==============================================================================
--- python/branches/py3k/Lib/test/test_smtpd.py	(original)
+++ python/branches/py3k/Lib/test/test_smtpd.py	Fri Dec  3 08:38:22 2010
@@ -121,6 +121,24 @@
         self.assertEqual(self.channel.socket.last,
                          b'451 Internal confusion\r\n')
 
+    def test_command_too_long(self):
+        self.write_line(b'MAIL from ' +
+                        b'a' * self.channel.command_size_limit +
+                        b'@example')
+        self.assertEqual(self.channel.socket.last,
+                         b'500 Error: line too long\r\n')
+
+    def test_data_too_long(self):
+        # Small hack. Setting limit to 2K octets here will save us some time.
+        self.channel.data_size_limit = 2048
+        self.write_line(b'MAIL From:eggs at example')
+        self.write_line(b'RCPT To:spam at example')
+        self.write_line(b'DATA')
+        self.write_line(b'A' * self.channel.data_size_limit +
+                        b'A\r\n.')
+        self.assertEqual(self.channel.socket.last,
+                         b'552 Error: Too much mail data\r\n')
+
     def test_need_MAIL(self):
         self.write_line(b'RCPT to:spam at example')
         self.assertEqual(self.channel.socket.last,

Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS	(original)
+++ python/branches/py3k/Misc/NEWS	Fri Dec  3 08:38:22 2010
@@ -33,6 +33,9 @@
 Library
 -------
 
+- Issue #1745035: Add a command size and data size limit to smtpd.py, to
+  prevent DoS attacks.  Patch by Savio Sena.
+
 - Issue #4925: Add filename to error message when executable can't be found in
   subprocess.
 


More information about the Python-checkins mailing list