Skip to content

Commit c114353

Browse files
committed
python#16042: CVE-2013-1752: smtplib fix for unlimited readline() from socket
1 parent f580d5b commit c114353

File tree

4 files changed

+43
-4
lines changed

4 files changed

+43
-4
lines changed

Lib/smtplib.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
SMTP_SSL_PORT = 465
6363
CRLF = "\r\n"
6464
bCRLF = b"\r\n"
65+
_MAXLINE = 8192 # more than 8 times larger than RFC 821, 4.5.3
6566

6667
OLDSTYLE_AUTH = re.compile(r"auth=(.*)", re.I)
6768

@@ -364,7 +365,7 @@ def getreply(self):
364365
self.file = self.sock.makefile('rb')
365366
while 1:
366367
try:
367-
line = self.file.readline()
368+
line = self.file.readline(_MAXLINE + 1)
368369
except socket.error as e:
369370
self.close()
370371
raise SMTPServerDisconnected("Connection unexpectedly closed: "
@@ -374,6 +375,8 @@ def getreply(self):
374375
raise SMTPServerDisconnected("Connection unexpectedly closed")
375376
if self.debuglevel > 0:
376377
print('reply:', repr(line), file=stderr)
378+
if len(line) > _MAXLINE:
379+
raise SMTPResponseException(500, "Line too long.")
377380
resp.append(line[4:].strip(b' \t\r\n'))
378381
code = line[:3]
379382
# Check that the error code is syntactically correct.

Lib/test/mock_socket.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,13 @@ class MockFile:
2121
"""
2222
def __init__(self, lines):
2323
self.lines = lines
24-
def readline(self):
25-
return self.lines.pop(0) + b'\r\n'
24+
def readline(self, limit=-1):
25+
result = self.lines.pop(0) + b'\r\n'
26+
if limit >= 0:
27+
# Re-insert the line, removing the \r\n we added.
28+
self.lines.insert(0, result[limit:-2])
29+
result = result[:limit]
30+
return result
2631
def close(self):
2732
pass
2833

Lib/test/test_smtplib.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,33 @@ def testFailingHELO(self):
569569
HOST, self.port, 'localhost', 3)
570570

571571

572+
@unittest.skipUnless(threading, 'Threading required for this test.')
573+
class TooLongLineTests(unittest.TestCase):
574+
respdata = b'250 OK' + (b'.' * smtplib._MAXLINE * 2) + b'\n'
575+
576+
def setUp(self):
577+
self.old_stdout = sys.stdout
578+
self.output = io.StringIO()
579+
sys.stdout = self.output
580+
581+
self.evt = threading.Event()
582+
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
583+
self.sock.settimeout(15)
584+
self.port = support.bind_port(self.sock)
585+
servargs = (self.evt, self.respdata, self.sock)
586+
threading.Thread(target=server, args=servargs).start()
587+
self.evt.wait()
588+
self.evt.clear()
589+
590+
def tearDown(self):
591+
self.evt.wait()
592+
sys.stdout = self.old_stdout
593+
594+
def testLineTooLong(self):
595+
self.assertRaises(smtplib.SMTPResponseException, smtplib.SMTP,
596+
HOST, self.port, 'localhost', 3)
597+
598+
572599
sim_users = {'Mr.A@somewhere.com':'John A',
573600
'Ms.B@xn--fo-fka.com':'Sally B',
574601
'Mrs.C@somewhereesle.com':'Ruth C',
@@ -885,7 +912,8 @@ def found_terminator(self):
885912
def test_main(verbose=None):
886913
support.run_unittest(GeneralTests, DebuggingServerTests,
887914
NonConnectingTests,
888-
BadHELOServerTests, SMTPSimTests)
915+
BadHELOServerTests, SMTPSimTests,
916+
TooLongLineTests)
889917

890918
if __name__ == '__main__':
891919
test_main()

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ Core and Builtins
5050
Library
5151
-------
5252

53+
- Issue #16042: CVE-2013-1752: smtplib: Limit amount of data read by
54+
limiting the call to readline(). Original patch by Christian Heimes.
55+
5356
- Issue #20317: ExitStack.__exit__ could create a self-referential loop if an
5457
exception raised by a cleanup operation already had its context set
5558
correctly (for example, by the @contextmanager decorator). The infinite

0 commit comments

Comments
 (0)