@@ -94,6 +94,10 @@ def __init__(self, conn):
9494 self .rest = None
9595 self .next_retr_data = RETR_DATA
9696 self .push ('220 welcome' )
97+ # We use this as the string IPv4 address to direct the client
98+ # to in response to a PASV command. To test security behavior.
99+ # https://bugs.python.org/issue43285/.
100+ self .fake_pasv_server_ip = '252.253.254.255'
97101
98102 def collect_incoming_data (self , data ):
99103 self .in_buffer .append (data )
@@ -136,7 +140,8 @@ def cmd_pasv(self, arg):
136140 sock .bind ((self .socket .getsockname ()[0 ], 0 ))
137141 sock .listen (5 )
138142 sock .settimeout (TIMEOUT )
139- ip , port = sock .getsockname ()[:2 ]
143+ port = sock .getsockname ()[1 ]
144+ ip = self .fake_pasv_server_ip
140145 ip = ip .replace ('.' , ',' ); p1 = port / 256 ; p2 = port % 256
141146 self .push ('227 entering passive mode (%s,%d,%d)' % (ip , p1 , p2 ))
142147 conn , addr = sock .accept ()
@@ -689,6 +694,26 @@ def test_makepasv(self):
689694 # IPv4 is in use, just make sure send_epsv has not been used
690695 self .assertEqual (self .server .handler_instance .last_received_cmd , 'pasv' )
691696
697+ def test_makepasv_issue43285_security_disabled (self ):
698+ """Test the opt-in to the old vulnerable behavior."""
699+ self .client .trust_server_pasv_ipv4_address = True
700+ bad_host , port = self .client .makepasv ()
701+ self .assertEqual (
702+ bad_host , self .server .handler_instance .fake_pasv_server_ip )
703+ # Opening and closing a connection keeps the dummy server happy
704+ # instead of timing out on accept.
705+ socket .create_connection ((self .client .sock .getpeername ()[0 ], port ),
706+ timeout = TIMEOUT ).close ()
707+
708+ def test_makepasv_issue43285_security_enabled_default (self ):
709+ self .assertFalse (self .client .trust_server_pasv_ipv4_address )
710+ trusted_host , port = self .client .makepasv ()
711+ self .assertNotEqual (
712+ trusted_host , self .server .handler_instance .fake_pasv_server_ip )
713+ # Opening and closing a connection keeps the dummy server happy
714+ # instead of timing out on accept.
715+ socket .create_connection ((trusted_host , port ), timeout = TIMEOUT ).close ()
716+
692717 def test_with_statement (self ):
693718 self .client .quit ()
694719
0 commit comments