diff options
author | Flühmann Tobias, INI-ON-NTO-COY-NNI <tobias.fluehmann@swisscom.com> | 2017-10-16 15:23:00 +0200 |
---|---|---|
committer | Miklos Fazekas <mfazekas@szemafor.com> | 2018-03-22 11:38:35 +0100 |
commit | 0c9ea12816cd703137a6769f3794b0bbe6da35e2 (patch) | |
tree | 96f28fc51005c4ea83d67b958f10f76e1c7e40b7 | |
parent | fe775266130989a5910449b4a1ad0bd7b07a6bc2 (diff) | |
download | net-ssh-0c9ea12816cd703137a6769f3794b0bbe6da35e2.tar.gz |
hmac instead of mac, confusing in networking environment
-rw-r--r-- | CHANGES.txt | 1 | ||||
-rw-r--r-- | lib/net/ssh/transport/packet_stream.rb | 92 |
2 files changed, 47 insertions, 46 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index aec012a..d3ae6bf 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -3,6 +3,7 @@ === 5.0.0.beta2 * Support for sha256 pubkey fingerprint [Tom Maher, #585] + * Don't try to load default_keys if key_data option is used [Josh Larson, #589] === 5.0.0.beta1 diff --git a/lib/net/ssh/transport/packet_stream.rb b/lib/net/ssh/transport/packet_stream.rb index 3888597..c592678 100644 --- a/lib/net/ssh/transport/packet_stream.rb +++ b/lib/net/ssh/transport/packet_stream.rb @@ -6,8 +6,8 @@ require 'net/ssh/transport/cipher_factory' require 'net/ssh/transport/hmac' require 'net/ssh/transport/state' -module Net - module SSH +module Net + module SSH module Transport # A module that builds additional functionality onto the Net::SSH::BufferedIo @@ -16,27 +16,27 @@ module Net # to allow for both blocking and non-blocking reads. module PacketStream PROXY_COMMAND_HOST_IP = '<no hostip for proxy command>'.freeze - + include BufferedIo - + def self.extended(object) object.__send__(:initialize_ssh) end - + # The map of "hints" that can be used to modify the behavior of the packet # stream. For instance, when authentication succeeds, an "authenticated" # hint is set, which is used to determine whether or not to compress the # data when using the "delayed" compression algorithm. attr_reader :hints - + # The server state object, which encapsulates the algorithms used to interpret # packets coming from the server. attr_reader :server - + # The client state object, which encapsulates the algorithms used to build # packets to send to the server. attr_reader :client - + # The name of the client (local) end of the socket, as reported by the # socket. def client_name @@ -58,7 +58,7 @@ module Net end end end - + # The IP address of the peer (remote) end of the socket, as reported by # the socket. def peer_ip @@ -70,13 +70,13 @@ module Net PROXY_COMMAND_HOST_IP end end - + # Returns true if the IO is available for reading, and false otherwise. def available_for_read? result = IO.select([self], nil, nil, 0) result && result.first.any? end - + # Returns the next full packet. If the mode parameter is :nonblock (the # default), then this will return immediately, whether a packet is # available or not, and will return nil if there is no packet ready to be @@ -87,7 +87,7 @@ module Net when :nonblock then packet = poll_next_packet return packet if packet - + if available_for_read? if fill <= 0 result = poll_next_packet @@ -99,77 +99,77 @@ module Net end end poll_next_packet - + when :block then loop do packet = poll_next_packet return packet if packet - + loop do result = IO.select([self]) or next break if result.first.any? end - + raise Net::SSH::Disconnect, "connection closed by remote host" if fill <= 0 end - + else raise ArgumentError, "expected :block or :nonblock, got #{mode.inspect}" end end - + # Enqueues a packet to be sent, and blocks until the entire packet is # sent. def send_packet(payload) enqueue_packet(payload) wait_for_pending_sends end - + # Enqueues a packet to be sent, but does not immediately send the packet. # The given payload is pre-processed according to the algorithms specified # in the client state (compression, cipher, and hmac). def enqueue_packet(payload) # try to compress the packet payload = client.compress(payload) - + # the length of the packet, minus the padding actual_length = 4 + payload.bytesize + 1 - + # compute the padding length padding_length = client.block_size - (actual_length % client.block_size) padding_length += client.block_size if padding_length < 4 - + # compute the packet length (sans the length field itself) packet_length = payload.bytesize + padding_length + 1 - + if packet_length < 16 padding_length += client.block_size packet_length = payload.bytesize + padding_length + 1 end - + padding = Array.new(padding_length) { rand(256) }.pack("C*") - + unencrypted_data = [packet_length, padding_length, payload, padding].pack("NCA*A*") mac = client.hmac.digest([client.sequence_number, unencrypted_data].pack("NA*")) - + encrypted_data = client.update_cipher(unencrypted_data) << client.final_cipher message = encrypted_data + mac - + debug { "queueing packet nr #{client.sequence_number} type #{payload.getbyte(0)} len #{packet_length}" } enqueue(message) - + client.increment(packet_length) - + self end - + # Performs any pending cleanup necessary on the IO and its associated # state objects. (See State#cleanup). def cleanup client.cleanup server.cleanup end - + # If the IO object requires a rekey operation (as indicated by either its # client or server state objects, see State#needs_rekey?), this will # yield. Otherwise, this does nothing. @@ -180,9 +180,9 @@ module Net server.reset! if server.needs_rekey? end end - + protected - + # Called when this module is used to extend an object. It initializes # the states and generally prepares the object for use as a packet stream. def initialize_ssh @@ -192,7 +192,7 @@ module Net @packet = nil initialize_buffered_io end - + # Tries to read the next packet. If there is insufficient data to read # an entire packet, this returns immediately, otherwise the packet is # read, post-processed according to the cipher, hmac, and compression @@ -203,43 +203,43 @@ module Net minimum = server.block_size < 4 ? 4 : server.block_size return nil if available < minimum data = read_available(minimum) - + # decipher it @packet = Net::SSH::Buffer.new(server.update_cipher(data)) @packet_length = @packet.read_long end - + need = @packet_length + 4 - server.block_size raise Net::SSH::Exception, "padding error, need #{need} block #{server.block_size}" if need % server.block_size != 0 - + return nil if available < need + server.hmac.mac_length - + if need > 0 # read the remainder of the packet and decrypt it. data = read_available(need) @packet.append(server.update_cipher(data)) end - + # get the hmac from the tail of the packet (if one exists), and # then validate it. real_hmac = read_available(server.hmac.mac_length) || "" - + @packet.append(server.final_cipher) padding_length = @packet.read_byte - + payload = @packet.read(@packet_length - padding_length - 1) - + my_computed_hmac = server.hmac.digest([server.sequence_number, @packet.content].pack("NA*")) - raise Net::SSH::Exception, "corrupted mac detected" if real_hmac != my_computed_hmac - + raise Net::SSH::Exception, "corrupted hmac detected" if real_hmac != my_computed_hmac + # try to decompress the payload, in case compression is active payload = server.decompress(payload) - + debug { "received packet nr #{server.sequence_number} type #{payload.getbyte(0)} len #{@packet_length}" } - + server.increment(@packet_length) @packet = nil - + return Packet.new(payload) end end |