diff --git a/paramiko/packet.py b/paramiko/packet.py index bf4cdb4..2fdc163 100644 --- a/paramiko/packet.py +++ b/paramiko/packet.py @@ -46,6 +46,7 @@ class Packetizer (object): self.__closed = False self.__dump_packets = False self.__need_rekey = False + self.__init_count = 0 # used for noticing when to re-key: self.__sent_bytes = 0 @@ -100,7 +101,11 @@ class Packetizer (object): self.__mac_key_out = mac_key self.__sent_bytes = 0 self.__sent_packets = 0 - self.__need_rekey = False + # wait until the reset happens in both directions before clearing rekey flag + self.__init_count |= 1 + if self.__init_count == 3: + self.__init_count = 0 + self.__need_rekey = False def set_inbound_cipher(self, block_engine, block_size, mac_engine, mac_size, mac_key): """ @@ -114,7 +119,11 @@ class Packetizer (object): self.__received_bytes = 0 self.__received_packets = 0 self.__received_packets_overflow = 0 - self.__need_rekey = False + # wait until the reset happens in both directions before clearing rekey flag + self.__init_count |= 2 + if self.__init_count == 3: + self.__init_count = 0 + self.__need_rekey = False def close(self): self.__closed = True diff --git a/paramiko/transport.py b/paramiko/transport.py index b436847..dc0632a 100644 --- a/paramiko/transport.py +++ b/paramiko/transport.py @@ -227,6 +227,7 @@ class BaseTransport (threading.Thread): self.expected_packet = 0 self.active = False self.initial_kex_done = False + self.in_kex = False self.lock = threading.Lock() # synchronization (always higher level than write_lock) self.channels = { } # (id -> Channel) self.channel_events = { } # (id -> Event) @@ -860,7 +861,7 @@ class BaseTransport (threading.Thread): def _send_message(self, data): self.packetizer.send_message(data) - if self.packetizer.need_rekey(): + if self.packetizer.need_rekey() and not self.in_kex: self._send_kex_init() def _send_user_message(self, data): @@ -933,7 +934,7 @@ class BaseTransport (threading.Thread): self.expected_packet = MSG_KEXINIT while self.active: - if self.packetizer.need_rekey(): + if self.packetizer.need_rekey() and not self.in_kex: self._send_kex_init() ptype, m = self.packetizer.read_message() if ptype == MSG_IGNORE: @@ -1050,6 +1051,7 @@ class BaseTransport (threading.Thread): kind of key negotiation we support. """ self.clear_to_send.clear() + self.in_kex = True if self.server_mode: if (self._modulus_pack is None) and ('diffie-hellman-group-exchange-sha1' in self._preferred_kex): # can't do group-exchange if we don't have a pack of potential primes @@ -1074,7 +1076,7 @@ class BaseTransport (threading.Thread): m.add('none') m.add('') m.add('') - m.add_boolean(0) + m.add_boolean(False) m.add_int(0) # save a copy for later (needed to compute a hash) self.local_kex_init = str(m) @@ -1212,6 +1214,8 @@ class BaseTransport (threading.Thread): else: mac_key = self._compute_key('E', mac_engine.digest_size) self.packetizer.set_outbound_cipher(engine, block_size, mac_engine, mac_size, mac_key) + if not self.packetizer.need_rekey(): + self.in_kex = False # we always expect to receive NEWKEYS now self.expected_packet = MSG_NEWKEYS @@ -1228,6 +1232,8 @@ class BaseTransport (threading.Thread): if self.completion_event != None: self.completion_event.set() # it's now okay to send data again (if this was a re-key) + if not self.packetizer.need_rekey(): + self.in_kex = False self.clear_to_send.set() return