diff options
| author | delano <delano@delanotes.com> | 2014-12-02 09:43:22 -0800 |
|---|---|---|
| committer | delano <delano@delanotes.com> | 2014-12-02 09:43:22 -0800 |
| commit | 1af5a4e8159b812c0d0afe1a12a53083e7e4d255 (patch) | |
| tree | 9208642512480fab488c617db2f93587b5f4b822 | |
| parent | 6931a9b045ef839a7dac2f1b6cbb8ab2587e582f (diff) | |
| parent | d30356e7be3262cda0ade193d9fd150d1e4308d4 (diff) | |
| download | net-ssh-1af5a4e8159b812c0d0afe1a12a53083e7e4d255.tar.gz | |
Resolved Rakefile merge
27 files changed, 436 insertions, 170 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 5a37932..02f7e2b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,9 @@ +=== 2.9.2-beta +* Remove advertised algorithms that were not working (ssh-rsa-cert-* *ed25519 acm*-gcm@openssh.com) [mfazekas] +* Unkown algorithms now ignored instead of failed [mfazekas] +* Asks for password with password auth (up to number_of_password_prompts) [mfazekas] +* Removed warnings [amatsuda] === 2.9.1 / 13 May 2014 diff --git a/README.rdoc b/README.rdoc index f7fd02e..a2c7525 100644 --- a/README.rdoc +++ b/README.rdoc @@ -171,6 +171,11 @@ Run a single test file like this: * JRuby 1.5: 99% tests pass (448 tests, 1846 assertions, 1 failures) +=== BUILDING GEM + +Since building the gem requires the private key if you want to build a .gem locally please use the NET_SSH_NOKEY=1 envirnoment variable: + + rake -T build NET_SSH_NOKEY=1 === PORT FORWARDING TESTS @@ -34,10 +34,16 @@ begin s.license = "MIT" - s.signing_key = File.join('/mnt/gem/', 'net-ssh-private_key.pem') - s.cert_chain = ['net-ssh-public_cert.pem'] + unless ENV['NET_SSH_NOKEY'] + signing_key = File.join('/mnt/gem/', 'net-ssh-private_key.pem') + s.signing_key = File.join('/mnt/gem/', 'net-ssh-private_key.pem') + s.cert_chain = ['net-ssh-public_cert.pem'] + unless (Rake.application.top_level_tasks & ['build','install']).empty? + raise "No key found at #{signing_key} for signing, use rake <taskname> NET_SSH_NOKEY=1 to build without key" unless File.exist?(signing_key) + end + end end - Jeweler::GemcutterTasks.new + Jeweler::RubygemsDotOrgTasks.new rescue LoadError puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler" end diff --git a/lib/net/ssh.rb b/lib/net/ssh.rb index d9a1f57..d4eb307 100644 --- a/lib/net/ssh.rb +++ b/lib/net/ssh.rb @@ -63,7 +63,7 @@ module Net VALID_OPTIONS = [ :auth_methods, :bind_address, :compression, :compression_level, :config, :encryption, :forward_agent, :hmac, :host_key, - :keepalive, :keepalive_interval, :kex, :keys, :key_data, + :keepalive, :keepalive_interval, :keepalive_maxcount, :kex, :keys, :key_data, :languages, :logger, :paranoid, :password, :port, :proxy, :rekey_blocks_limit,:rekey_limit, :rekey_packet_limit, :timeout, :verbose, :global_known_hosts_file, :user_known_hosts_file, :host_key_alias, @@ -130,6 +130,7 @@ module Net # the keepalive_interval seconds. Defaults to +false+. # :keepalive_interval => the interval seconds for keepalive. # Defaults to +300+ seconds. + # :keepalive_countmax => the maximun number of keepalive packet miss allowed. # * :kex => the key exchange algorithm (or algorithms) to use # * :keys => an array of file names of private keys to use for publickey # and hostbased authentication diff --git a/lib/net/ssh/authentication/agent/socket.rb b/lib/net/ssh/authentication/agent/socket.rb index c80099e..7ca6d92 100644 --- a/lib/net/ssh/authentication/agent/socket.rb +++ b/lib/net/ssh/authentication/agent/socket.rb @@ -94,10 +94,16 @@ module Net; module SSH; module Authentication identities = [] body.read_long.times do - key = Buffer.new(body.read_string).read_key - key.extend(Comment) - key.comment = body.read_string - identities.push key + key_str = body.read_string + comment_str = body.read_string + begin + key = Buffer.new(key_str).read_key + key.extend(Comment) + key.comment = comment_str + identities.push key + rescue NotImplementedError => e + error { "ignoring unimplemented key:#{e.message} #{comment_str}" } + end end return identities diff --git a/lib/net/ssh/authentication/methods/password.rb b/lib/net/ssh/authentication/methods/password.rb index ad1eed7..6c4f174 100644 --- a/lib/net/ssh/authentication/methods/password.rb +++ b/lib/net/ssh/authentication/methods/password.rb @@ -1,4 +1,5 @@ require 'net/ssh/errors' +require 'net/ssh/prompt' require 'net/ssh/authentication/methods/abstract' module Net @@ -8,25 +9,36 @@ module Net # Implements the "password" SSH authentication method. class Password < Abstract + include Prompt + # Attempt to authenticate the given user for the given service. If - # the password parameter is nil, this will never do anything except - # return false. + # the password parameter is nil, this will ask for password def authenticate(next_service, username, password=nil) - return false unless password + retries = 0 + max_retries = get_max_retries + return false if !password && max_retries == 0 - send_message(userauth_request(username, next_service, "password", false, password)) - message = session.next_message + begin + password_to_send = password || ask_password(username) - case message.type - when USERAUTH_SUCCESS - debug { "password succeeded" } - return true - when USERAUTH_FAILURE + send_message(userauth_request(username, next_service, "password", false, password_to_send)) + message = session.next_message + retries += 1 + + if message.type == USERAUTH_FAILURE debug { "password failed" } raise Net::SSH::Authentication::DisallowedMethod unless message[:authentications].split(/,/).include? 'password' + password = nil + end + end until (message.type != USERAUTH_FAILURE || retries >= max_retries) + case message.type + when USERAUTH_SUCCESS + debug { "password succeeded" } + return true + when USERAUTH_FAILURE return false when USERAUTH_PASSWD_CHANGEREQ debug { "password change request received, failing" } @@ -35,6 +47,19 @@ module Net raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})" end end + + private + + NUMBER_OF_PASSWORD_PROMPTS = 3 + + def ask_password(username) + echo = false + prompt("#{username}@#{session.transport.host}'s password:", echo) + end + + def get_max_retries + (session.transport.options||{})[:number_of_password_prompts] || NUMBER_OF_PASSWORD_PROMPTS + end end end diff --git a/lib/net/ssh/buffer.rb b/lib/net/ssh/buffer.rb index 1ee8a0a..c17da78 100644 --- a/lib/net/ssh/buffer.rb +++ b/lib/net/ssh/buffer.rb @@ -255,7 +255,7 @@ module Net; module SSH key.e = read_bignum key.n = read_bignum - when /^ecdsa\-sha2\-(\w*)$/, /^ssh-ed25519(-cert-v01@openssh.com)?$/ + when /^ecdsa\-sha2\-(\w*)$/ unless defined?(OpenSSL::PKey::EC) raise NotImplementedError, "unsupported key type `#{type}'" else diff --git a/lib/net/ssh/config.rb b/lib/net/ssh/config.rb index 137b8e3..0622472 100644 --- a/lib/net/ssh/config.rb +++ b/lib/net/ssh/config.rb @@ -31,6 +31,7 @@ module Net; module SSH # * RekeyLimit => :rekey_limit # * User => :user # * UserKnownHostsFile => :user_known_hosts_file + # * NumberOfPasswordPrompts => :number_of_password_prompts # # Note that you will never need to use this class directly--you can control # whether the OpenSSH configuration files are read by passing the :config @@ -59,7 +60,7 @@ module Net; module SSH # #default_files), translates the resulting hash into the options # recognized by Net::SSH, and returns them. def for(host, files=default_files) - hash = translate(files.inject({}) { |settings, file| + translate(files.inject({}) { |settings, file| load(file, host, settings) }) end @@ -76,7 +77,6 @@ module Net; module SSH globals = {} matched_host = nil - multi_host = [] seen_host = false IO.foreach(file) do |line| next if line =~ /^\s*(?:#.*)?$/ @@ -220,6 +220,8 @@ module Net; module SSH when 'sendenv' multi_send_env = value.to_s.split(/\s+/) hash[:send_env] = multi_send_env.map { |e| Regexp.new pattern2regex(e).source, false } + when 'numberofpasswordprompts' + hash[:number_of_password_prompts] = value.to_i end hash end diff --git a/lib/net/ssh/connection/keepalive.rb b/lib/net/ssh/connection/keepalive.rb new file mode 100644 index 0000000..6b0daa8 --- /dev/null +++ b/lib/net/ssh/connection/keepalive.rb @@ -0,0 +1,47 @@ +module Net; module SSH; module Connection + +module Keepalive + # Default IO.select timeout threshold + DEFAULT_IO_SELECT_TIMEOUT = 300 + + def initialize_keepalive + @last_keepalive_sent_at = nil + @unresponded_keepalive_count = 0 + end + + def keepalive_enabled? + options[:keepalive] + end + + def keepalive_interval + options[:keepalive_interval] || DEFAULT_IO_SELECT_TIMEOUT + end + + def should_send_keepalive? + return false unless keepalive_enabled? + return true unless @last_keepalive_sent_at + Time.now - @last_keepalive_sent_at >= keepalive_interval + end + + def keepalive_maxcount + (options[:keepalive_maxcount] || 3).to_i + end + + def send_keepalive_as_needed(readers, writers) + return unless readers.nil? && writers.nil? + return unless should_send_keepalive? + info { "sending keepalive #{@unresponded_keepalive_count}" } + + @unresponded_keepalive_count += 1 + send_global_request("keepalive@openssh.com") { |success, response| + @unresponded_keepalive_count = 0 + } + if keepalive_maxcount > 0 && @unresponded_keepalive_count > keepalive_maxcount + error { "Timeout, server #{host} not responding. Missed #{@unresponded_keepalive_count} timeouts." } + raise Net::SSH::Timeout, "Timeout, server #{host} not responding." + end + @last_keepalive_sent_at = Time.now + end +end + +end; end; end
\ No newline at end of file diff --git a/lib/net/ssh/connection/session.rb b/lib/net/ssh/connection/session.rb index 9be74e8..3137514 100644 --- a/lib/net/ssh/connection/session.rb +++ b/lib/net/ssh/connection/session.rb @@ -3,6 +3,7 @@ require 'net/ssh/ruby_compat' require 'net/ssh/connection/channel' require 'net/ssh/connection/constants' require 'net/ssh/service/forward' +require 'net/ssh/connection/keepalive' module Net; module SSH; module Connection @@ -23,10 +24,7 @@ module Net; module SSH; module Connection # ssh.exec! "/etc/init.d/some_process start" # end class Session - include Constants, Loggable - - # Default IO.select timeout threshold - DEFAULT_IO_SELECT_TIMEOUT = 300 + include Constants, Loggable, Keepalive # The underlying transport layer abstraction (see Net::SSH::Transport::Session). attr_reader :transport @@ -79,7 +77,7 @@ module Net; module SSH; module Connection @max_pkt_size = (options.has_key?(:max_pkt_size) ? options[:max_pkt_size] : 0x8000) @max_win_size = (options.has_key?(:max_win_size) ? options[:max_win_size] : 0x20000) - @last_keepalive_sent_at = nil + initialize_keepalive end # Retrieves a custom property from this instance. This can be used to @@ -598,29 +596,10 @@ module Net; module SSH; module Connection def io_select_wait(wait) return wait if wait - return wait unless options[:keepalive] + return wait unless keepalive_enabled? keepalive_interval end - def keepalive_interval - options[:keepalive_interval] || DEFAULT_IO_SELECT_TIMEOUT - end - - def should_send_keepalive? - return false unless options[:keepalive] - return true unless @last_keepalive_sent_at - Time.now - @last_keepalive_sent_at >= keepalive_interval - end - - def send_keepalive_as_needed(readers, writers) - return unless readers.nil? && writers.nil? - return unless should_send_keepalive? - info { "sending keepalive" } - msg = Net::SSH::Buffer.from(:byte, Packet::IGNORE, :string, "keepalive") - send_message(msg) - @last_keepalive_sent_at = Time.now - end - MAP = Constants.constants.inject({}) do |memo, name| value = const_get(name) next unless Integer === value diff --git a/lib/net/ssh/errors.rb b/lib/net/ssh/errors.rb index 8f5a136..910c961 100644 --- a/lib/net/ssh/errors.rb +++ b/lib/net/ssh/errors.rb @@ -14,6 +14,10 @@ module Net; module SSH # unexpectedly. class Disconnect < Exception; end + # This exception is raised when the remote host has disconnected/ + # timeouted unexpectedly. + class Timeout < Disconnect; end + # This exception is primarily used internally, but if you have a channel # request handler (see Net::SSH::Connection::Channel#on_request) that you # want to fail in such a way that the server knows it failed, you can diff --git a/lib/net/ssh/known_hosts.rb b/lib/net/ssh/known_hosts.rb index 7ee253a..f91131e 100644 --- a/lib/net/ssh/known_hosts.rb +++ b/lib/net/ssh/known_hosts.rb @@ -15,17 +15,9 @@ module Net; module SSH SUPPORTED_TYPE = %w(ssh-rsa ssh-dss ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 - ecdsa-sha2-nistp521 - ssh-ed25519-cert-v01@openssh.com - ssh-rsa-cert-v01@openssh.com - ssh-rsa-cert-v00@openssh.com - ssh-ed25519 - ) + ecdsa-sha2-nistp521) else - SUPPORTED_TYPE = %w(ssh-rsa ssh-dss - ssh-rsa-cert-v01@openssh.com - ssh-rsa-cert-v00@openssh.com - ) + SUPPORTED_TYPE = %w(ssh-rsa ssh-dss) end diff --git a/lib/net/ssh/service/forward.rb b/lib/net/ssh/service/forward.rb index 0d5d55d..b6349c0 100644 --- a/lib/net/ssh/service/forward.rb +++ b/lib/net/ssh/service/forward.rb @@ -88,13 +88,13 @@ module Net; module SSH; module Service end prepare_client(client, channel, :local) - + channel.on_open_failed do |ch, code, description| channel.error { "could not establish direct channel: #{description} (#{code})" } channel[:socket].close end end - + local_port end @@ -131,22 +131,52 @@ module Net; module SSH; module Service # the port number. The assigned port will show up in the # #active_remotes # list. # + # remote_host is interpreted by the server per RFC 4254, which has these + # special values: + # + # - "" means that connections are to be accepted on all protocol + # families supported by the SSH implementation. + # - "0.0.0.0" means to listen on all IPv4 addresses. + # - "::" means to listen on all IPv6 addresses. + # - "localhost" means to listen on all protocol families supported by + # the SSH implementation on loopback addresses only ([RFC3330] and + # [RFC3513]). + # - "127.0.0.1" and "::1" indicate listening on the loopback + # interfaces for IPv4 and IPv6, respectively. + # + # You may pass a block that will be called when the the port forward + # request receives a response. This block will be passed the remote_port + # that was actually bound to, or nil if the binding failed. If the block + # returns :no_exception, the "failed binding" exception will not be thrown. + # # If you want to block until the port is active, you could do something # like this: # - # old_active_remotes = ssh.forward.active_remotes - # ssh.forward.remote(80, "www.google.com", 0, "0.0.0.0") - # ssh.loop { !(ssh.forward.active_remotes.length > old_active_remotes.length) } - # assigned_port = (ssh.forward.active_remotes - old_active_remotes).first[0] + # got_remote_port = nil + # remote(port, host, remote_port, remote_host) do |actual_remote_port| + # got_remote_port = actual_remote_port || :error + # :no_exception # will yield the exception on my own thread + # end + # session.loop { !got_remote_port } + # if got_remote_port == :error + # raise Net::SSH::Exception, "remote forwarding request failed" + # end + # def remote(port, host, remote_port, remote_host="127.0.0.1") session.send_global_request("tcpip-forward", :string, remote_host, :long, remote_port) do |success, response| if success remote_port = response.read_long if remote_port == 0 debug { "remote forward from remote #{remote_host}:#{remote_port} to #{host}:#{port} established" } @remote_forwarded_ports[[remote_port, remote_host]] = Remote.new(host, port) + yield remote_port, remote_host if block_given? else - error { "remote forwarding request failed" } - raise Net::SSH::Exception, "remote forwarding request failed" + instruction = if block_given? + yield :error + end + unless instruction == :no_exception + error { "remote forwarding request failed" } + raise Net::SSH::Exception, "remote forwarding request failed" + end end end end @@ -183,6 +213,16 @@ module Net; module SSH; module Service @remote_forwarded_ports.keys end + # Returns all active remote forwarded ports and where they forward to. The + # returned value is a hash from [<forwarding port on the local host>, <local forwarding address>] + # to [<port on the remote host>, <remote bind address>]. + def active_remote_destinations + @remote_forwarded_ports.inject({}) do |result, (remote, local)| + result[[local.port, local.host]] = remote + result + end + end + # Enables SSH agent forwarding on the given channel. The forwarded agent # will remain active even after the channel closes--the channel is only # used as the transport for enabling the forwarded connection. You should @@ -216,7 +256,7 @@ module Net; module SSH; module Service end private - + # Perform setup operations that are common to all forwarded channels. # +client+ is a socket, +channel+ is the channel that was just created, # and +type+ is an arbitrary string describing the type of the channel. @@ -228,11 +268,11 @@ module Net; module SSH; module Service session.listen_to(client) channel[:socket] = client - channel.on_data do |ch, data| + channel.on_data do |ch, data| debug { "data:#{data.length} on #{type} forwarded channel" } ch[:socket].enqueue(data) end - + # Handles server close on the sending side by Miklós Fazekas channel.on_eof do |ch| debug { "eof #{type} on #{type} forwarded channel" } @@ -251,7 +291,7 @@ module Net; module SSH; module Service debug { "enotconn in on_eof => shallowing exception:#{e}" } end end - + channel.on_close do |ch| debug { "closing #{type} forwarded channel" } ch[:socket].close if !client.closed? diff --git a/lib/net/ssh/transport/algorithms.rb b/lib/net/ssh/transport/algorithms.rb index f4aec2f..1f9a360 100644 --- a/lib/net/ssh/transport/algorithms.rb +++ b/lib/net/ssh/transport/algorithms.rb @@ -42,14 +42,12 @@ module Net; module SSH; module Transport camellia192-ctr@openssh.org camellia256-ctr@openssh.org cast128-ctr blowfish-ctr 3des-ctr - aes256-gcm@openssh.com aes128-gcm@openssh.com ), + :hmac => %w(hmac-sha1 hmac-md5 hmac-sha1-96 hmac-md5-96 hmac-ripemd160 hmac-ripemd160@openssh.com hmac-sha2-256 hmac-sha2-512 hmac-sha2-256-96 - hmac-sha2-512-96 none - hmac-sha2-512-etm@openssh.com hmac-sha2-256-etm@openssh.com - umac-128-etm@openssh.com), + hmac-sha2-512-96 none), :compression => %w(none zlib@openssh.com zlib), :language => %w() @@ -57,9 +55,7 @@ module Net; module SSH; module Transport if defined?(OpenSSL::PKey::EC) ALGORITHMS[:host_key] += %w(ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 - ecdsa-sha2-nistp521 - ssh-ed25519-cert-v01@openssh.com - ssh-ed25519) + ecdsa-sha2-nistp521) ALGORITHMS[:kex] += %w(ecdh-sha2-nistp256 ecdh-sha2-nistp384 ecdh-sha2-nistp521 @@ -221,8 +217,13 @@ module Net; module SSH; module Transport # apply the preferred algorithm order, if any if options[algorithm] algorithms[algorithm] = Array(options[algorithm]).compact.uniq - invalid = algorithms[algorithm].detect { |name| !ALGORITHMS[algorithm].include?(name) } - raise NotImplementedError, "unsupported #{algorithm} algorithm: `#{invalid}'" if invalid + unsupported = [] + algorithms[algorithm].select! do |name| + supported = ALGORITHMS[algorithm].include?(name) + unsupported << name unless supported + supported + end + lwarn { "unsupported #{algorithm} algorithm: `#{unsupported}'" } unless unsupported.empty? # make sure all of our supported algorithms are tacked onto the # end, so that if the user tries to give a list of which none are diff --git a/lib/net/ssh/transport/cipher_factory.rb b/lib/net/ssh/transport/cipher_factory.rb index 912079e..4d9ab45 100644 --- a/lib/net/ssh/transport/cipher_factory.rb +++ b/lib/net/ssh/transport/cipher_factory.rb @@ -41,9 +41,6 @@ module Net; module SSH; module Transport "camellia192-ctr@openssh.org" => "camellia-192-ecb", "camellia256-ctr@openssh.org" => "camellia-256-ecb", - "aes256-gcm@openssh.com" => "aes-256-gcm", - "aes128-gcm@openssh.com" => "aes-128-gcm", - "none" => "none", } diff --git a/lib/net/ssh/transport/packet_stream.rb b/lib/net/ssh/transport/packet_stream.rb index 7d5fcab..8e9ffe5 100644 --- a/lib/net/ssh/transport/packet_stream.rb +++ b/lib/net/ssh/transport/packet_stream.rb @@ -14,6 +14,8 @@ module Net; module SSH; module Transport # per the SSH2 protocol. It also adds an abstraction for polling packets, # 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) @@ -64,7 +66,7 @@ module Net; module SSH; module Transport addr = getpeername Socket.getnameinfo(addr, Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV).first else - "<no hostip for proxy command>" + PROXY_COMMAND_HOST_IP end end diff --git a/lib/net/ssh/transport/session.rb b/lib/net/ssh/transport/session.rb index 05253c9..76dd18b 100644 --- a/lib/net/ssh/transport/session.rb +++ b/lib/net/ssh/transport/session.rb @@ -93,11 +93,16 @@ module Net; module SSH; module Transport @host_as_string ||= begin string = "#{host}" string = "[#{string}]:#{port}" if port != DEFAULT_PORT - if socket.peer_ip != host - string2 = socket.peer_ip + + peer_ip = socket.peer_ip + + if peer_ip != Net::SSH::Transport::PacketStream::PROXY_COMMAND_HOST_IP && + peer_ip != host + string2 = peer_ip string2 = "[#{string2}]:#{port}" if port != DEFAULT_PORT string << "," << string2 end + string end end diff --git a/lib/net/ssh/version.rb b/lib/net/ssh/version.rb index b802f28..3f96132 100644 --- a/lib/net/ssh/version.rb +++ b/lib/net/ssh/version.rb @@ -16,15 +16,15 @@ module Net; module SSH # A convenience method for instantiating a new Version instance with the # given +major+, +minor+, and +tiny+ components. - def self.[](major, minor, tiny) - new(major, minor, tiny) + def self.[](major, minor, tiny, pre = nil) + new(major, minor, tiny, pre) end attr_reader :major, :minor, :tiny # Create a new Version object with the given components. - def initialize(major, minor, tiny) - @major, @minor, @tiny = major, minor, tiny + def initialize(major, minor, tiny, pre = nil) + @major, @minor, @tiny, @pre = major, minor, tiny, pre end # Compare this version to the given +version+ object. @@ -35,7 +35,7 @@ module Net; module SSH # Converts this version object to a string, where each of the three # version components are joined by the '.' character. E.g., 2.0.0. def to_s - @to_s ||= [@major, @minor, @tiny].join(".") + @to_s ||= [@major, @minor, @tiny, @pre].compact.join(".") end # Converts this version to a canonical integer that may be compared @@ -51,10 +51,14 @@ module Net; module SSH MINOR = 9 # The tiny component of this version of the Net::SSH library - TINY = 1 + TINY = 2 + + # The prerelease component of this version of the Net::SSH library + # nil allowed + PRE = "beta" # The current version of the Net::SSH library as a Version instance - CURRENT = new(MAJOR, MINOR, TINY) + CURRENT = new(*[MAJOR, MINOR, TINY, PRE].compact) # The current version of the Net::SSH library as a String STRING = CURRENT.to_s diff --git a/net-ssh.gemspec b/net-ssh.gemspec index edbbeaa..99ea38d 100644 --- a/net-ssh.gemspec +++ b/net-ssh.gemspec @@ -2,15 +2,17 @@ # DO NOT EDIT THIS FILE DIRECTLY # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' # -*- encoding: utf-8 -*- +# stub: net-ssh 2.9.2.beta ruby lib Gem::Specification.new do |s| s.name = "net-ssh" - s.version = "2.9.1" + s.version = "2.9.2.beta" - s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= + s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version= + s.require_paths = ["lib"] s.authors = ["Jamis Buck", "Delano Mandelbaum"] s.cert_chain = ["gem-public_cert.pem"] - s.date = "2014-05-13" + s.date = "2014-12-02" s.description = "Net::SSH: a pure-Ruby implementation of the SSH2 client protocol. It allows you to write programs that invoke and interact with processes on remote servers, via SSH2." s.email = "net-ssh@solutious.com" s.extra_rdoc_files = [ @@ -46,6 +48,7 @@ Gem::Specification.new do |s| "lib/net/ssh/config.rb", "lib/net/ssh/connection/channel.rb", "lib/net/ssh/connection/constants.rb", + "lib/net/ssh/connection/keepalive.rb", "lib/net/ssh/connection/session.rb", "lib/net/ssh/connection/term.rb", "lib/net/ssh/errors.rb", @@ -177,14 +180,13 @@ Gem::Specification.new do |s| ] s.homepage = "https://github.com/net-ssh/net-ssh" s.licenses = ["MIT"] - s.require_paths = ["lib"] s.rubyforge_project = "net-ssh" - s.rubygems_version = "1.8.23" + s.rubygems_version = "2.2.2" s.signing_key = "/mnt/gem/gem-private_key.pem" s.summary = "Net::SSH: a pure-Ruby implementation of the SSH2 client protocol." if s.respond_to? :specification_version then - s.specification_version = 3 + s.specification_version = 4 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_development_dependency(%q<test-unit>, [">= 0"]) diff --git a/test/authentication/methods/test_password.rb b/test/authentication/methods/test_password.rb index 4ba8207..3448b5a 100644 --- a/test/authentication/methods/test_password.rb +++ b/test/authentication/methods/test_password.rb @@ -1,7 +1,9 @@ require 'common' require 'net/ssh/authentication/methods/password' +require 'net/ssh/authentication/session' require 'authentication/methods/common' + module Authentication; module Methods class TestPassword < Test::Unit::TestCase @@ -24,6 +26,47 @@ module Authentication; module Methods end end + def test_authenticate_ask_for_password_for_second_time_when_password_is_incorrect + transport.expect do |t,packet| + assert_equal USERAUTH_REQUEST, packet.type + assert_equal "jamis", packet.read_string + assert_equal "ssh-connection", packet.read_string + assert_equal "password", packet.read_string + assert_equal false, packet.read_bool + assert_equal "the-password", packet.read_string + t.return(USERAUTH_FAILURE, :string, "publickey,password") + + t.expect do |t2, packet2| + assert_equal USERAUTH_REQUEST, packet2.type + assert_equal "jamis", packet2.read_string + assert_equal "ssh-connection", packet2.read_string + assert_equal "password", packet2.read_string + assert_equal false, packet2.read_bool + assert_equal "the-password-2", packet2.read_string + t.return(USERAUTH_SUCCESS) + end + end + + subject.expects(:prompt).with("jamis@'s password:", false).returns("the-password-2") + subject.authenticate("ssh-connection", "jamis", "the-password") + end + + def test_authenticate_ask_for_password_if_not_given + transport.expect do |t,packet| + assert_equal USERAUTH_REQUEST, packet.type + assert_equal "bill", packet.read_string + assert_equal "ssh-connection", packet.read_string + assert_equal "password", packet.read_string + assert_equal false, packet.read_bool + assert_equal "good-password", packet.read_string + t.return(USERAUTH_SUCCESS) + end + + transport.instance_eval { @host='testhost' } + subject.expects(:prompt).with("bill@testhost's password:", false).returns("good-password") + subject.authenticate("ssh-connection", "bill", nil) + end + def test_authenticate_when_password_is_acceptible_should_return_true transport.expect do |t,packet| assert_equal USERAUTH_REQUEST, packet.type diff --git a/test/authentication/test_agent.rb b/test/authentication/test_agent.rb index 1e24d5e..1613727 100644 --- a/test/authentication/test_agent.rb +++ b/test/authentication/test_agent.rb @@ -110,6 +110,25 @@ module Authentication assert_equal "Okay, but not the best", result.last.comment end + def test_identities_should_ignore_unimplemented_ones + key1 = key + key2 = OpenSSL::PKey::DSA.new(512) + key2.to_blob[0..5]='badkey' + key3 = OpenSSL::PKey::DSA.new(512) + + socket.expect do |s, type, buffer| + assert_equal SSH2_AGENT_REQUEST_IDENTITIES, type + s.return(SSH2_AGENT_IDENTITIES_ANSWER, :long, 3, :string, Net::SSH::Buffer.from(:key, key1), :string, "My favorite key", :string, Net::SSH::Buffer.from(:key, key2), :string, "bad", :string, Net::SSH::Buffer.from(:key, key3), :string, "Okay, but not the best") + end + + result = agent.identities + assert_equal 2,result.size + assert_equal key1.to_blob, result.first.to_blob + assert_equal key3.to_blob, result.last.to_blob + assert_equal "My favorite key", result.first.comment + assert_equal "Okay, but not the best", result.last.comment + end + def test_close_should_close_socket socket.expects(:close) agent.close diff --git a/test/connection/test_session.rb b/test/connection/test_session.rb index 8af6cd3..268afb9 100644 --- a/test/connection/test_session.rb +++ b/test/connection/test_session.rb @@ -366,12 +366,29 @@ module Connection def test_process_should_call_enqueue_message_if_io_select_timed_out timeout = Net::SSH::Connection::Session::DEFAULT_IO_SELECT_TIMEOUT options = { :keepalive => true } - expected_packet = P(:byte, Net::SSH::Packet::IGNORE, :string, "keepalive") + expected_packet = P(:byte, Net::SSH::Packet::GLOBAL_REQUEST, :string, "keepalive@openssh.com", :bool, true) IO.stubs(:select).with([socket],[],nil,timeout).returns(nil) - transport.expects(:enqueue_message).with{ |msg| msg.content == expected_packet.content } + transport.expects(:enqueue_message).with{ |msg| msg.content == expected_packet.content } session(options).process end + def test_process_should_raise_if_keepalives_not_answered + timeout = Net::SSH::Connection::Session::DEFAULT_IO_SELECT_TIMEOUT + options = { :keepalive => true, :keepalive_interval => 300, :keepalive_maxcount => 3 } + expected_packet = P(:byte, Net::SSH::Packet::GLOBAL_REQUEST, :string, "keepalive@openssh.com", :bool, true) + [1,2,3].each do |i| + Time.stubs(:now).returns(i*300) + IO.stubs(:select).with([socket],[],nil,timeout).returns(nil) + transport.expects(:enqueue_message).with{ |msg| msg.content == expected_packet.content } + session(options).process + end + + Time.stubs(:now).returns(4*300) + IO.stubs(:select).with([socket],[],nil,timeout).returns(nil) + transport.expects(:enqueue_message).with{ |msg| msg.content == expected_packet.content } + assert_raises(Net::SSH::Timeout) { session(options).process } + end + def test_process_should_not_call_enqueue_message_unless_io_select_timed_out timeout = Net::SSH::Connection::Session::DEFAULT_IO_SELECT_TIMEOUT options = { :keepalive => true } diff --git a/test/manual/test_forward.rb b/test/manual/test_forward.rb index 5794077..7e864e2 100644 --- a/test/manual/test_forward.rb +++ b/test/manual/test_forward.rb @@ -3,15 +3,15 @@ # Tests for the following patch: # # http://github.com/net-ssh/net-ssh/tree/portfwfix -# +# # It fixes 3 issues, regarding closing forwarded ports: -# +# # 1.) if client closes a forwarded connection, but the server is reading, net-ssh terminates with IOError socket closed. # 2.) if client force closes (RST) a forwarded connection, but server is reading, net-ssh terminates with # 3.) if server closes the sending side, the on_eof is not handled. -# +# # More info: -# +# # http://net-ssh.lighthouseapp.com/projects/36253/tickets/7 require 'common' @@ -21,26 +21,26 @@ require 'timeout' require 'tempfile' class TestForward < Test::Unit::TestCase - + def localhost 'localhost' end - + def ssh_start_params [localhost ,ENV['USER'], {:keys => "~/.ssh/id_rsa", :verbose => :debug}] end - + def start_server_sending_lot_of_data(exceptions) server = TCPServer.open(0) Thread.start do loop do Thread.start(server.accept) do |client| begin - 10000.times do |i| + 10000.times do |i| client.puts "item#{i}" end client.close - rescue + rescue exceptions << $! raise end @@ -49,14 +49,14 @@ class TestForward < Test::Unit::TestCase end return server end - + def start_server_closing_soon(exceptions=nil) server = TCPServer.open(0) Thread.start do loop do Thread.start(server.accept) do |client| begin - client.recv(1024) + client.recv(1024) client.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, [1, 0].pack("ii")) client.close rescue @@ -68,20 +68,20 @@ class TestForward < Test::Unit::TestCase end return server end - + def test_local_ephemeral_port_should_work_correctly session = Net::SSH.start(*ssh_start_params) - + assert_nothing_raised do assigned_port = session.forward.local(0, localhost, 22) assert_not_nil assigned_port assert_operator assigned_port, :>, 0 end end - + def test_remote_ephemeral_port_should_work_correctly session = Net::SSH.start(*ssh_start_params) - + assert_nothing_raised do session.forward.remote(22, localhost, 0, localhost) session.loop { !(session.forward.active_remotes.length > 0) } @@ -90,9 +90,58 @@ class TestForward < Test::Unit::TestCase assert_operator assigned_port, :>, 0 end end - + + def test_remote_callback_should_fire + session = Net::SSH.start(*ssh_start_params) + + assert_nothing_raised do + got_port = nil + session.forward.remote(22, localhost, 0, localhost) do |port| + got_port = port + end + session.loop { !(session.forward.active_remotes.length > 0) } + assert_operator session.forward.active_remote_destinations.length, :==, 1 + assert_operator session.forward.active_remote_destinations.keys.first, :==, [ 22, localhost ] + assert_operator session.forward.active_remote_destinations.values.first, :==, [ got_port, localhost ] + assert_operator session.forward.active_remotes.first, :==, [ got_port, localhost ] + assigned_port = session.forward.active_remotes.first[0] + assert_operator got_port, :==, assigned_port + assert_not_nil assigned_port + assert_operator assigned_port, :>, 0 + end + end + + def test_remote_callback_should_fire_on_error_and_still_throw_exception + session = Net::SSH.start(*ssh_start_params) + + assert_nothing_raised do + session.forward.remote(22, localhost, 22, localhost) do |port| + assert_operator port, :==, :error + end + end + assert_raises(Net::SSH::Exception) do + session.loop { true } + end + end + + def test_remote_callback_should_fire_on_error_but_not_throw_exception_if_asked_not_to + session = Net::SSH.start(*ssh_start_params) + + assert_nothing_raised do + got_port = nil + session.forward.remote(22, localhost, 22, localhost) do |port| + assert_operator port, :==, :error + got_port = port + :no_exception + end + session.loop { !got_port } + assert_operator port, :==, :error + assert_operator session.forward.active_remotes.length, :==, 0 + end + end + def test_loop_should_not_abort_when_local_side_of_forward_is_closed - session = Net::SSH.start(*ssh_start_params) + session = Net::SSH.start(*ssh_start_params) server_exc = Queue.new server = start_server_sending_lot_of_data(server_exc) remote_port = server.addr[1] @@ -112,10 +161,10 @@ class TestForward < Test::Unit::TestCase session.loop(0.1) { client_done.empty? } assert_equal "Broken pipe", "#{server_exc.pop}" unless server_exc.empty? end - + def test_loop_should_not_abort_when_local_side_of_forward_is_reset session = Net::SSH.start(*ssh_start_params) - server_exc = Queue.new + server_exc = Queue.new server = start_server_sending_lot_of_data(server_exc) remote_port = server.addr[1] local_port = 0 # request ephemeral port @@ -143,9 +192,9 @@ class TestForward < Test::Unit::TestCase yield UNIXServer.open(path) File.delete(path) end if defined?(UNIXServer) - + def test_forward_local_unix_socket_to_remote_port - session = Net::SSH.start(*ssh_start_params) + session = Net::SSH.start(*ssh_start_params) server_exc = Queue.new server = start_server_sending_lot_of_data(server_exc) remote_port = server.addr[1] @@ -174,7 +223,7 @@ class TestForward < Test::Unit::TestCase end if defined?(UNIXSocket) def test_loop_should_not_abort_when_server_side_of_forward_is_closed - session = Net::SSH.start(*ssh_start_params) + session = Net::SSH.start(*ssh_start_params) server = start_server_closing_soon remote_port = server.addr[1] local_port = 0 # request ephemeral port @@ -183,18 +232,18 @@ class TestForward < Test::Unit::TestCase Thread.start do begin client = TCPSocket.new(localhost, local_port) - 1.times do |i| + 1.times do |i| client.puts "item#{i}" end client.close sleep(0.1) - ensure + ensure client_done << true end end session.loop(0.1) { client_done.empty? } end - + def start_server server = TCPServer.open(0) Thread.start do @@ -206,9 +255,9 @@ class TestForward < Test::Unit::TestCase end return server end - + def test_server_eof_should_be_handled - session = Net::SSH.start(*ssh_start_params) + session = Net::SSH.start(*ssh_start_params) server = start_server do |client| client.write "This is a small message!" client.close diff --git a/test/test_config.rb b/test/test_config.rb index 623281e..9b3eeb8 100644 --- a/test/test_config.rb +++ b/test/test_config.rb @@ -93,7 +93,8 @@ class TestConfig < Test::Unit::TestCase 'port' => 1234, 'pubkeyauthentication' => true, 'rekeylimit' => 1024, - 'sendenv' => "LC_*" + 'sendenv' => "LC_*", + 'numberofpasswordprompts' => '123' } net_ssh = Net::SSH::Config.translate(open_ssh) @@ -111,6 +112,7 @@ class TestConfig < Test::Unit::TestCase assert_equal 1024, net_ssh[:rekey_limit] assert_equal "127.0.0.1", net_ssh[:bind_address] assert_equal [/^LC_.*$/], net_ssh[:send_env] + assert_equal 123, net_ssh[:number_of_password_prompts] end def test_translate_should_turn_off_authentication_methods diff --git a/test/transport/test_algorithms.rb b/test/transport/test_algorithms.rb index 630c348..1be7c7c 100644 --- a/test/transport/test_algorithms.rb +++ b/test/transport/test_algorithms.rb @@ -17,16 +17,11 @@ module Transport end def test_constructor_should_build_default_list_of_preferred_algorithms - if defined?(OpenSSL::PKey::EC) - assert_equal %w(ssh-rsa ssh-dss ssh-rsa-cert-v01@openssh.com ssh-rsa-cert-v00@openssh.com ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521 ssh-ed25519-cert-v01@openssh.com ssh-ed25519), algorithms[:host_key] - assert_equal %w(diffie-hellman-group-exchange-sha1 diffie-hellman-group1-sha1 diffie-hellman-group14-sha1 diffie-hellman-group-exchange-sha256 ecdh-sha2-nistp256 ecdh-sha2-nistp384 ecdh-sha2-nistp521 curve25519-sha256@libssh.org), algorithms[:kex] - else - assert_equal %w(ssh-rsa ssh-dss ssh-rsa-cert-v01@openssh.com ssh-rsa-cert-v00@openssh.com), algorithms[:host_key] - assert_equal %w(diffie-hellman-group-exchange-sha1 diffie-hellman-group1-sha1 diffie-hellman-group14-sha1 diffie-hellman-group-exchange-sha256 aes256-gcm@openssh.com aes128-gcm@openssh.com), algorithms[:kex] - end - assert_equal %w(aes128-cbc 3des-cbc blowfish-cbc cast128-cbc aes192-cbc aes256-cbc rijndael-cbc@lysator.liu.se idea-cbc none arcfour128 arcfour256 arcfour aes128-ctr aes192-ctr aes256-ctr camellia128-cbc camellia192-cbc camellia256-cbc camellia128-cbc@openssh.org camellia192-cbc@openssh.org camellia256-cbc@openssh.org camellia128-ctr camellia192-ctr camellia256-ctr camellia128-ctr@openssh.org camellia192-ctr@openssh.org camellia256-ctr@openssh.org cast128-ctr blowfish-ctr 3des-ctr aes256-gcm@openssh.com aes128-gcm@openssh.com), algorithms[:encryption] + assert_equal %w(ssh-rsa ssh-dss ssh-rsa-cert-v01@openssh.com ssh-rsa-cert-v00@openssh.com)+ec_host_keys, algorithms[:host_key] + assert_equal %w(diffie-hellman-group-exchange-sha1 diffie-hellman-group1-sha1 diffie-hellman-group14-sha1 diffie-hellman-group-exchange-sha256)+ec_kex, algorithms[:kex] + assert_equal %w(aes128-cbc 3des-cbc blowfish-cbc cast128-cbc aes192-cbc aes256-cbc rijndael-cbc@lysator.liu.se idea-cbc none arcfour128 arcfour256 arcfour aes128-ctr aes192-ctr aes256-ctr camellia128-cbc camellia192-cbc camellia256-cbc camellia128-cbc@openssh.org camellia192-cbc@openssh.org camellia256-cbc@openssh.org camellia128-ctr camellia192-ctr camellia256-ctr camellia128-ctr@openssh.org camellia192-ctr@openssh.org camellia256-ctr@openssh.org cast128-ctr blowfish-ctr 3des-ctr), algorithms[:encryption] if defined?(OpenSSL::Digest::SHA256) - assert_equal %w(hmac-sha1 hmac-md5 hmac-sha1-96 hmac-md5-96 hmac-ripemd160 hmac-ripemd160@openssh.com hmac-sha2-256 hmac-sha2-512 hmac-sha2-256-96 hmac-sha2-512-96 none hmac-sha2-512-etm@openssh.com hmac-sha2-256-etm@openssh.com umac-128-etm@openssh.com), algorithms[:hmac] + assert_equal %w(hmac-sha1 hmac-md5 hmac-sha1-96 hmac-md5-96 hmac-ripemd160 hmac-ripemd160@openssh.com hmac-sha2-256 hmac-sha2-512 hmac-sha2-256-96 hmac-sha2-512-96 none), algorithms[:hmac] else assert_equal %w(hmac-sha1 hmac-md5 hmac-sha1-96 hmac-md5-96 hmac-ripemd160 hmac-ripemd160@openssh.com none umac-128-etm@openssh.com), algorithms[:hmac] end assert_equal %w(none zlib@openssh.com zlib), algorithms[:compression] @@ -41,60 +36,65 @@ module Transport end def test_constructor_with_preferred_host_key_type_should_put_preferred_host_key_type_first - if defined?(OpenSSL::PKey::EC) - assert_equal %w(ssh-dss ssh-rsa ssh-rsa-cert-v01@openssh.com ssh-rsa-cert-v00@openssh.com ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521 ssh-ed25519-cert-v01@openssh.com ssh-ed25519), algorithms(:host_key => "ssh-dss")[:host_key] - else - assert_equal %w(ssh-dss ssh-rsa ssh-rsa-cert-v01@openssh.com ssh-rsa-cert-v00@openssh.com), algorithms(:host_key => "ssh-dss")[:host_key] - end + assert_equal %w(ssh-dss ssh-rsa ssh-rsa-cert-v01@openssh.com ssh-rsa-cert-v00@openssh.com)+ec_host_keys, algorithms(:host_key => "ssh-dss")[:host_key] end def test_constructor_with_known_hosts_reporting_known_host_key_should_use_that_host_key_type Net::SSH::KnownHosts.expects(:search_for).with("net.ssh.test,127.0.0.1", {}).returns([stub("key", :ssh_type => "ssh-dss")]) + assert_equal %w(ssh-dss ssh-rsa ssh-rsa-cert-v01@openssh.com ssh-rsa-cert-v00@openssh.com )+ec_host_keys, algorithms[:host_key] + end + + def ec_host_keys if defined?(OpenSSL::PKey::EC) - assert_equal %w(ssh-dss ssh-rsa ssh-rsa-cert-v01@openssh.com ssh-rsa-cert-v00@openssh.com ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521 ssh-ed25519-cert-v01@openssh.com ssh-ed25519), algorithms[:host_key] + %w(ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521) else - assert_equal %w(ssh-dss ssh-rsa), algorithms[:host_key] + [] end end - def test_constructor_with_unrecognized_host_key_type_should_raise_exception - assert_raises(NotImplementedError) { algorithms(:host_key => "bogus") } + def test_constructor_with_unrecognized_host_key_type_should_return_whats_supported + assert_equal %w(ssh-rsa ssh-dss ssh-rsa-cert-v01@openssh.com ssh-rsa-cert-v00@openssh.com )+ec_host_keys, algorithms(:host_key => "bogus ssh-rsa")[:host_key] end - def test_constructor_with_preferred_kex_should_put_preferred_kex_first + def ec_kex if defined?(OpenSSL::PKey::EC) - assert_equal %w(diffie-hellman-group1-sha1 diffie-hellman-group-exchange-sha1 diffie-hellman-group14-sha1 diffie-hellman-group-exchange-sha256 ecdh-sha2-nistp256 ecdh-sha2-nistp384 ecdh-sha2-nistp521 curve25519-sha256@libssh.org), algorithms(:kex => "diffie-hellman-group1-sha1")[:kex] + %w(ecdh-sha2-nistp256 ecdh-sha2-nistp384 ecdh-sha2-nistp521 curve25519-sha256@libssh.org) else - assert_equal %w(diffie-hellman-group1-sha1 diffie-hellman-group-exchange-sha1 diffie-hellman-group14-sha1 diffie-hellman-group-exchange-sha256), algorithms(:kex => "diffie-hellman-group1-sha1")[:kex] + [] end end + def test_constructor_with_preferred_kex_should_put_preferred_kex_first + assert_equal %w(diffie-hellman-group1-sha1 diffie-hellman-group-exchange-sha1 diffie-hellman-group14-sha1 diffie-hellman-group-exchange-sha256)+ec_kex, algorithms(:kex => "diffie-hellman-group1-sha1")[:kex] + end + def test_constructor_with_unrecognized_kex_should_raise_exception - assert_raises(NotImplementedError) { algorithms(:kex => "bogus") } + assert_equal %w(diffie-hellman-group1-sha1 diffie-hellman-group-exchange-sha1 diffie-hellman-group14-sha1 diffie-hellman-group-exchange-sha256)+ec_kex, algorithms(:kex => %w(bogus diffie-hellman-group1-sha1))[:kex] end def test_constructor_with_preferred_encryption_should_put_preferred_encryption_first - assert_equal %w(aes256-cbc aes128-cbc 3des-cbc blowfish-cbc cast128-cbc aes192-cbc rijndael-cbc@lysator.liu.se idea-cbc none arcfour128 arcfour256 arcfour aes128-ctr aes192-ctr aes256-ctr camellia128-cbc camellia192-cbc camellia256-cbc camellia128-cbc@openssh.org camellia192-cbc@openssh.org camellia256-cbc@openssh.org camellia128-ctr camellia192-ctr camellia256-ctr camellia128-ctr@openssh.org camellia192-ctr@openssh.org camellia256-ctr@openssh.org cast128-ctr blowfish-ctr 3des-ctr aes256-gcm@openssh.com aes128-gcm@openssh.com), algorithms(:encryption => "aes256-cbc")[:encryption] + assert_equal %w(aes256-cbc aes128-cbc 3des-cbc blowfish-cbc cast128-cbc aes192-cbc rijndael-cbc@lysator.liu.se idea-cbc none arcfour128 arcfour256 arcfour aes128-ctr aes192-ctr aes256-ctr camellia128-cbc camellia192-cbc camellia256-cbc camellia128-cbc@openssh.org camellia192-cbc@openssh.org camellia256-cbc@openssh.org camellia128-ctr camellia192-ctr camellia256-ctr camellia128-ctr@openssh.org camellia192-ctr@openssh.org camellia256-ctr@openssh.org cast128-ctr blowfish-ctr 3des-ctr), algorithms(:encryption => "aes256-cbc")[:encryption] end def test_constructor_with_multiple_preferred_encryption_should_put_all_preferred_encryption_first - assert_equal %w(aes256-cbc 3des-cbc idea-cbc aes128-cbc blowfish-cbc cast128-cbc aes192-cbc rijndael-cbc@lysator.liu.se none arcfour128 arcfour256 arcfour aes128-ctr aes192-ctr aes256-ctr camellia128-cbc camellia192-cbc camellia256-cbc camellia128-cbc@openssh.org camellia192-cbc@openssh.org camellia256-cbc@openssh.org camellia128-ctr camellia192-ctr camellia256-ctr camellia128-ctr@openssh.org camellia192-ctr@openssh.org camellia256-ctr@openssh.org cast128-ctr blowfish-ctr 3des-ctr aes256-gcm@openssh.com aes128-gcm@openssh.com), algorithms(:encryption => %w(aes256-cbc 3des-cbc idea-cbc))[:encryption] + assert_equal %w(aes256-cbc 3des-cbc idea-cbc aes128-cbc blowfish-cbc cast128-cbc aes192-cbc rijndael-cbc@lysator.liu.se none arcfour128 arcfour256 arcfour aes128-ctr aes192-ctr aes256-ctr camellia128-cbc camellia192-cbc camellia256-cbc camellia128-cbc@openssh.org camellia192-cbc@openssh.org camellia256-cbc@openssh.org camellia128-ctr camellia192-ctr camellia256-ctr camellia128-ctr@openssh.org camellia192-ctr@openssh.org camellia256-ctr@openssh.org cast128-ctr blowfish-ctr 3des-ctr), algorithms(:encryption => %w(aes256-cbc 3des-cbc idea-cbc))[:encryption] end - def test_constructor_with_unrecognized_encryption_should_raise_exception - assert_raises(NotImplementedError) { algorithms(:encryption => "bogus") } + def test_constructor_with_unrecognized_encryption_should_keep_whats_supported + assert_equal %w(aes256-cbc aes128-cbc 3des-cbc blowfish-cbc cast128-cbc aes192-cbc rijndael-cbc@lysator.liu.se idea-cbc none arcfour128 arcfour256 arcfour aes128-ctr aes192-ctr aes256-ctr camellia128-cbc camellia192-cbc camellia256-cbc camellia128-cbc@openssh.org camellia192-cbc@openssh.org camellia256-cbc@openssh.org camellia128-ctr camellia192-ctr camellia256-ctr camellia128-ctr@openssh.org camellia192-ctr@openssh.org camellia256-ctr@openssh.org cast128-ctr blowfish-ctr 3des-ctr), algorithms(:encryption => %w(bogus aes256-cbc))[:encryption] end def test_constructor_with_preferred_hmac_should_put_preferred_hmac_first - assert_equal %w(hmac-md5-96 hmac-sha1 hmac-md5 hmac-sha1-96 hmac-ripemd160 hmac-ripemd160@openssh.com hmac-sha2-256 hmac-sha2-512 hmac-sha2-256-96 hmac-sha2-512-96 none hmac-sha2-512-etm@openssh.com hmac-sha2-256-etm@openssh.com umac-128-etm@openssh.com), algorithms(:hmac => "hmac-md5-96")[:hmac] + assert_equal %w(hmac-md5-96 hmac-sha1 hmac-md5 hmac-sha1-96 hmac-ripemd160 hmac-ripemd160@openssh.com hmac-sha2-256 hmac-sha2-512 hmac-sha2-256-96 hmac-sha2-512-96 none), algorithms(:hmac => "hmac-md5-96")[:hmac] end def test_constructor_with_multiple_preferred_hmac_should_put_all_preferred_hmac_first - assert_equal %w(hmac-md5-96 hmac-sha1-96 hmac-sha1 hmac-md5 hmac-ripemd160 hmac-ripemd160@openssh.com hmac-sha2-256 hmac-sha2-512 hmac-sha2-256-96 hmac-sha2-512-96 none hmac-sha2-512-etm@openssh.com hmac-sha2-256-etm@openssh.com umac-128-etm@openssh.com), algorithms(:hmac => %w(hmac-md5-96 hmac-sha1-96))[:hmac] + assert_equal %w(hmac-md5-96 hmac-sha1-96 hmac-sha1 hmac-md5 hmac-ripemd160 hmac-ripemd160@openssh.com hmac-sha2-256 hmac-sha2-512 hmac-sha2-256-96 hmac-sha2-512-96 none), algorithms(:hmac => %w(hmac-md5-96 hmac-sha1-96))[:hmac] end - def test_constructor_with_unrecognized_hmac_should_raise_exception - assert_raises(NotImplementedError) { algorithms(:hmac => "bogus") } + def test_constructor_with_unrecognized_hmac_should_ignore_those + assert_equal %w(hmac-md5-96 hmac-sha1 hmac-md5 hmac-sha1-96 hmac-ripemd160 hmac-ripemd160@openssh.com hmac-sha2-256 hmac-sha2-512 hmac-sha2-256-96 hmac-sha2-512-96 none), + algorithms(:hmac => "hmac-md5-96")[:hmac] end def test_constructor_with_preferred_compression_should_put_preferred_compression_first @@ -109,8 +109,8 @@ module Transport assert_equal %w(zlib@openssh.com zlib none), algorithms(:compression => true)[:compression] end - def test_constructor_with_unrecognized_compression_should_raise_exception - assert_raises(NotImplementedError) { algorithms(:compression => "bogus") } + def test_constructor_with_unrecognized_compression_should_return_whats_supported + assert_equal %w(none zlib zlib@openssh.com), algorithms(:compression => %w(bogus none zlib))[:compression] end def test_initial_state_should_be_neither_pending_nor_initialized @@ -288,17 +288,12 @@ module Transport def assert_kexinit(buffer, options={}) assert_equal KEXINIT, buffer.type assert_equal 16, buffer.read(16).length - if defined?(OpenSSL::PKey::EC) - assert_equal options[:kex] || "diffie-hellman-group-exchange-sha1,diffie-hellman-group1-sha1,diffie-hellman-group14-sha1,diffie-hellman-group-exchange-sha256,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,curve25519-sha256@libssh.org", buffer.read_string - assert_equal options[:host_key] || "ssh-rsa,ssh-dss,ssh-rsa-cert-v01@openssh.com,ssh-rsa-cert-v00@openssh.com,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-ed25519-cert-v01@openssh.com,ssh-ed25519", buffer.read_string - else - assert_equal options[:kex] || "diffie-hellman-group-exchange-sha1,diffie-hellman-group1-sha1,diffie-hellman-group14-sha1,diffie-hellman-group-exchange-sha256", buffer.read_string - assert_equal options[:host_key] || "ssh-rsa,ssh-dss", buffer.read_string - end - assert_equal options[:encryption_client] || "aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se,idea-cbc,none,arcfour128,arcfour256,arcfour,aes128-ctr,aes192-ctr,aes256-ctr,camellia128-cbc,camellia192-cbc,camellia256-cbc,camellia128-cbc@openssh.org,camellia192-cbc@openssh.org,camellia256-cbc@openssh.org,camellia128-ctr,camellia192-ctr,camellia256-ctr,camellia128-ctr@openssh.org,camellia192-ctr@openssh.org,camellia256-ctr@openssh.org,cast128-ctr,blowfish-ctr,3des-ctr,aes256-gcm@openssh.com,aes128-gcm@openssh.com", buffer.read_string - assert_equal options[:encryption_server] || "aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se,idea-cbc,none,arcfour128,arcfour256,arcfour,aes128-ctr,aes192-ctr,aes256-ctr,camellia128-cbc,camellia192-cbc,camellia256-cbc,camellia128-cbc@openssh.org,camellia192-cbc@openssh.org,camellia256-cbc@openssh.org,camellia128-ctr,camellia192-ctr,camellia256-ctr,camellia128-ctr@openssh.org,camellia192-ctr@openssh.org,camellia256-ctr@openssh.org,cast128-ctr,blowfish-ctr,3des-ctr,aes256-gcm@openssh.com,aes128-gcm@openssh.com", buffer.read_string - assert_equal options[:hmac_client] || "hmac-sha1,hmac-md5,hmac-sha1-96,hmac-md5-96,hmac-ripemd160,hmac-ripemd160@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha2-256-96,hmac-sha2-512-96,none,hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com", buffer.read_string - assert_equal options[:hmac_server] || "hmac-sha1,hmac-md5,hmac-sha1-96,hmac-md5-96,hmac-ripemd160,hmac-ripemd160@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha2-256-96,hmac-sha2-512-96,none,hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com", buffer.read_string + assert_equal options[:kex] || (%w(diffie-hellman-group-exchange-sha1 diffie-hellman-group1-sha1 diffie-hellman-group14-sha1 diffie-hellman-group-exchange-sha256)+ec_kex).join(','), buffer.read_string + assert_equal options[:host_key] || (%w(ssh-rsa ssh-dss ssh-rsa-cert-v01@openssh.com ssh-rsa-cert-v00@openssh.com)+ec_host_keys).join(','), buffer.read_string + assert_equal options[:encryption_client] || "aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se,idea-cbc,none,arcfour128,arcfour256,arcfour,aes128-ctr,aes192-ctr,aes256-ctr,camellia128-cbc,camellia192-cbc,camellia256-cbc,camellia128-cbc@openssh.org,camellia192-cbc@openssh.org,camellia256-cbc@openssh.org,camellia128-ctr,camellia192-ctr,camellia256-ctr,camellia128-ctr@openssh.org,camellia192-ctr@openssh.org,camellia256-ctr@openssh.org,cast128-ctr,blowfish-ctr,3des-ctr", buffer.read_string + assert_equal options[:encryption_server] || "aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se,idea-cbc,none,arcfour128,arcfour256,arcfour,aes128-ctr,aes192-ctr,aes256-ctr,camellia128-cbc,camellia192-cbc,camellia256-cbc,camellia128-cbc@openssh.org,camellia192-cbc@openssh.org,camellia256-cbc@openssh.org,camellia128-ctr,camellia192-ctr,camellia256-ctr,camellia128-ctr@openssh.org,camellia192-ctr@openssh.org,camellia256-ctr@openssh.org,cast128-ctr,blowfish-ctr,3des-ctr", buffer.read_string + assert_equal options[:hmac_client] || "hmac-sha1,hmac-md5,hmac-sha1-96,hmac-md5-96,hmac-ripemd160,hmac-ripemd160@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha2-256-96,hmac-sha2-512-96,none", buffer.read_string + assert_equal options[:hmac_server] || "hmac-sha1,hmac-md5,hmac-sha1-96,hmac-md5-96,hmac-ripemd160,hmac-ripemd160@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha2-256-96,hmac-sha2-512-96,none", buffer.read_string assert_equal options[:compression_client] || "none,zlib@openssh.com,zlib", buffer.read_string assert_equal options[:compression_server] || "none,zlib@openssh.com,zlib", buffer.read_string assert_equal options[:language_client] || "", buffer.read_string diff --git a/test/transport/test_packet_stream.rb b/test/transport/test_packet_stream.rb index c83d9b0..bcd65b8 100644 --- a/test/transport/test_packet_stream.rb +++ b/test/transport/test_packet_stream.rb @@ -45,6 +45,12 @@ module Transport assert_equal "1.2.3.4", stream.peer_ip end + def test_peer_ip_should_return_no_hostip_when_socket_has_no_peername + assert_equal false, stream.respond_to?(:getpeername) + assert_equal Net::SSH::Transport::PacketStream::PROXY_COMMAND_HOST_IP, stream.peer_ip + assert_equal '<no hostip for proxy command>', stream.peer_ip + end + def test_available_for_read_should_return_nontrue_when_select_fails IO.expects(:select).returns(nil) assert !stream.available_for_read? @@ -1678,7 +1684,7 @@ module Transport hmacs = Net::SSH::Transport::HMAC::MAP.keys ciphers.each do |cipher_name| - unless Net::SSH::Transport::CipherFactory.supported?(cipher_name) + unless Net::SSH::Transport::CipherFactory.supported?(cipher_name) && PACKETS.has_key?(cipher_name) puts "Skipping packet stream test for #{cipher_name}" next end diff --git a/test/transport/test_session.rb b/test/transport/test_session.rb index 02176da..88e8402 100644 --- a/test/transport/test_session.rb +++ b/test/transport/test_session.rb @@ -69,6 +69,18 @@ module Transport assert_equal "[1.2.3.4]:1234", session.host_as_string end + def test_host_as_string_should_return_only_host_when_proxy_command_is_set + session!(:host => "1.2.3.4") + socket.stubs(:peer_ip).returns(Net::SSH::Transport::PacketStream::PROXY_COMMAND_HOST_IP) + assert_equal "1.2.3.4", session.host_as_string + end + + def test_host_as_string_should_return_only_host_and_port_when_host_is_ip_and_port_is_not_default_and_proxy_command_is_set + session!(:host => "1.2.3.4", :port => 1234) + socket.stubs(:peer_ip).returns(Net::SSH::Transport::PacketStream::PROXY_COMMAND_HOST_IP) + assert_equal "[1.2.3.4]:1234", session.host_as_string + end + def test_close_should_cleanup_and_close_socket session! socket.expects(:cleanup) |
