Skip to content

Commit e5b5edf

Browse files
committed
Issue python#19781: ftplib now supports SSLContext.check_hostname and server name
indication for TLS/SSL connections.
1 parent 1aa9a75 commit e5b5edf

File tree

4 files changed

+51
-3
lines changed

4 files changed

+51
-3
lines changed

Doc/library/ftplib.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ The module defines the following items:
9494
.. versionchanged:: 3.3
9595
*source_address* parameter was added.
9696

97+
.. versionchanged:: 3.4
98+
The class now supports hostname check with
99+
:attr:`SSLContext.check_hostname` and *Server Name Indicator* (see
100+
:data:`~ssl.HAS_SNI`).
101+
97102
Here's a sample session using the :class:`FTP_TLS` class:
98103

99104
>>> from ftplib import FTP_TLS
@@ -427,6 +432,11 @@ FTP_TLS Objects
427432
Set up secure control connection by using TLS or SSL, depending on what
428433
specified in :meth:`ssl_version` attribute.
429434

435+
.. versionchanged:: 3.4
436+
The method now supports hostname check with
437+
:attr:`SSLContext.check_hostname` and *Server Name Indicator* (see
438+
:data:`~ssl.HAS_SNI`).
439+
430440
.. method:: FTP_TLS.ccc()
431441

432442
Revert control channel back to plaintext. This can be useful to take

Lib/ftplib.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -748,7 +748,9 @@ def auth(self):
748748
resp = self.voidcmd('AUTH TLS')
749749
else:
750750
resp = self.voidcmd('AUTH SSL')
751-
self.sock = self.context.wrap_socket(self.sock)
751+
server_hostname = self.host if ssl.HAS_SNI else None
752+
self.sock = self.context.wrap_socket(self.sock,
753+
server_hostname=server_hostname)
752754
self.file = self.sock.makefile(mode='r', encoding=self.encoding)
753755
return resp
754756

@@ -787,7 +789,9 @@ def prot_c(self):
787789
def ntransfercmd(self, cmd, rest=None):
788790
conn, size = FTP.ntransfercmd(self, cmd, rest)
789791
if self._prot_p:
790-
conn = self.context.wrap_socket(conn)
792+
server_hostname = self.host if ssl.HAS_SNI else None
793+
conn = self.context.wrap_socket(conn,
794+
server_hostname=server_hostname)
791795
return conn, size
792796

793797
def abort(self):

Lib/test/test_ftplib.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,8 @@ def handle_error(self):
301301

302302
if ssl is not None:
303303

304-
CERTFILE = os.path.join(os.path.dirname(__file__), "keycert.pem")
304+
CERTFILE = os.path.join(os.path.dirname(__file__), "keycert3.pem")
305+
CAFILE = os.path.join(os.path.dirname(__file__), "pycacert.pem")
305306

306307
class SSLConnection(asyncore.dispatcher):
307308
"""An asyncore.dispatcher subclass supporting TLS/SSL."""
@@ -923,6 +924,36 @@ def test_ccc(self):
923924
self.client.ccc()
924925
self.assertRaises(ValueError, self.client.sock.unwrap)
925926

927+
def test_check_hostname(self):
928+
self.client.quit()
929+
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
930+
ctx.verify_mode = ssl.CERT_REQUIRED
931+
ctx.check_hostname = True
932+
ctx.load_verify_locations(CAFILE)
933+
self.client = ftplib.FTP_TLS(context=ctx, timeout=TIMEOUT)
934+
935+
# 127.0.0.1 doesn't match SAN
936+
self.client.connect(self.server.host, self.server.port)
937+
with self.assertRaises(ssl.CertificateError):
938+
self.client.auth()
939+
# exception quits connection
940+
941+
self.client.connect(self.server.host, self.server.port)
942+
self.client.prot_p()
943+
with self.assertRaises(ssl.CertificateError):
944+
with self.client.transfercmd("list") as sock:
945+
pass
946+
self.client.quit()
947+
948+
self.client.connect("localhost", self.server.port)
949+
self.client.auth()
950+
self.client.quit()
951+
952+
self.client.connect("localhost", self.server.port)
953+
self.client.prot_p()
954+
with self.client.transfercmd("list") as sock:
955+
pass
956+
926957

927958
class TestTimeouts(TestCase):
928959

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ Core and Builtins
1818
Library
1919
-------
2020

21+
- Issue #19781: ftplib now supports SSLContext.check_hostname and server name
22+
indication for TLS/SSL connections.
23+
2124
- Issue #19509: Add SSLContext.check_hostname to match the peer's certificate
2225
with server_hostname on handshake.
2326

0 commit comments

Comments
 (0)