Blame SOURCES/00372-CVE-2021-4189.patch

9b3a7c
diff --git a/Lib/ftplib.py b/Lib/ftplib.py
9b3a7c
index 2ff251a..385e432 100644
9b3a7c
--- a/Lib/ftplib.py
9b3a7c
+++ b/Lib/ftplib.py
9b3a7c
@@ -104,6 +104,8 @@ class FTP:
9b3a7c
     welcome = None
9b3a7c
     passiveserver = 1
9b3a7c
     encoding = "latin-1"
9b3a7c
+    # Disables https://bugs.python.org/issue43285 security if set to True.
9b3a7c
+    trust_server_pasv_ipv4_address = False
9b3a7c
 
9b3a7c
     # Initialization method (called by class instantiation).
9b3a7c
     # Initialize host to localhost, port to standard ftp port
9b3a7c
@@ -333,8 +335,13 @@ class FTP:
9b3a7c
         return sock
9b3a7c
 
9b3a7c
     def makepasv(self):
9b3a7c
+        """Internal: Does the PASV or EPSV handshake -> (address, port)"""
9b3a7c
         if self.af == socket.AF_INET:
9b3a7c
-            host, port = parse227(self.sendcmd('PASV'))
9b3a7c
+            untrusted_host, port = parse227(self.sendcmd('PASV'))
9b3a7c
+            if self.trust_server_pasv_ipv4_address:
9b3a7c
+                host = untrusted_host
9b3a7c
+            else:
9b3a7c
+                host = self.sock.getpeername()[0]
9b3a7c
         else:
9b3a7c
             host, port = parse229(self.sendcmd('EPSV'), self.sock.getpeername())
9b3a7c
         return host, port
9b3a7c
diff --git a/Lib/test/test_ftplib.py b/Lib/test/test_ftplib.py
9b3a7c
index 4ff2f71..3ca7cc1 100644
9b3a7c
--- a/Lib/test/test_ftplib.py
9b3a7c
+++ b/Lib/test/test_ftplib.py
9b3a7c
@@ -94,6 +94,10 @@ class DummyFTPHandler(asynchat.async_chat):
9b3a7c
         self.rest = None
9b3a7c
         self.next_retr_data = RETR_DATA
9b3a7c
         self.push('220 welcome')
9b3a7c
+        # We use this as the string IPv4 address to direct the client
9b3a7c
+        # to in response to a PASV command.  To test security behavior.
9b3a7c
+        # https://bugs.python.org/issue43285/.
9b3a7c
+        self.fake_pasv_server_ip = '252.253.254.255'
9b3a7c
 
9b3a7c
     def collect_incoming_data(self, data):
9b3a7c
         self.in_buffer.append(data)
9b3a7c
@@ -136,7 +140,8 @@ class DummyFTPHandler(asynchat.async_chat):
9b3a7c
             sock.bind((self.socket.getsockname()[0], 0))
9b3a7c
             sock.listen()
9b3a7c
             sock.settimeout(TIMEOUT)
9b3a7c
-            ip, port = sock.getsockname()[:2]
9b3a7c
+            port = sock.getsockname()[1]
9b3a7c
+            ip = self.fake_pasv_server_ip
9b3a7c
             ip = ip.replace('.', ','); p1 = port / 256; p2 = port % 256
9b3a7c
             self.push('227 entering passive mode (%s,%d,%d)' %(ip, p1, p2))
9b3a7c
             conn, addr = sock.accept()
9b3a7c
@@ -694,6 +699,26 @@ class TestFTPClass(TestCase):
9b3a7c
         # IPv4 is in use, just make sure send_epsv has not been used
9b3a7c
         self.assertEqual(self.server.handler_instance.last_received_cmd, 'pasv')
9b3a7c
 
9b3a7c
+    def test_makepasv_issue43285_security_disabled(self):
9b3a7c
+        """Test the opt-in to the old vulnerable behavior."""
9b3a7c
+        self.client.trust_server_pasv_ipv4_address = True
9b3a7c
+        bad_host, port = self.client.makepasv()
9b3a7c
+        self.assertEqual(
9b3a7c
+                bad_host, self.server.handler_instance.fake_pasv_server_ip)
9b3a7c
+        # Opening and closing a connection keeps the dummy server happy
9b3a7c
+        # instead of timing out on accept.
9b3a7c
+        socket.create_connection((self.client.sock.getpeername()[0], port),
9b3a7c
+                                 timeout=TIMEOUT).close()
9b3a7c
+
9b3a7c
+    def test_makepasv_issue43285_security_enabled_default(self):
9b3a7c
+        self.assertFalse(self.client.trust_server_pasv_ipv4_address)
9b3a7c
+        trusted_host, port = self.client.makepasv()
9b3a7c
+        self.assertNotEqual(
9b3a7c
+                trusted_host, self.server.handler_instance.fake_pasv_server_ip)
9b3a7c
+        # Opening and closing a connection keeps the dummy server happy
9b3a7c
+        # instead of timing out on accept.
9b3a7c
+        socket.create_connection((trusted_host, port), timeout=TIMEOUT).close()
9b3a7c
+
9b3a7c
     def test_with_statement(self):
9b3a7c
         self.client.quit()
9b3a7c