Hello community, here is the log from the commit of package rubygem-net-ssh for openSUSE:Factory checked in at 2019-03-04 09:20:24 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rubygem-net-ssh (Old) and /work/SRC/openSUSE:Factory/.rubygem-net-ssh.new.28833 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-net-ssh" Mon Mar 4 09:20:24 2019 rev:28 rq:679527 version:5.1.0 Changes: -------- --- /work/SRC/openSUSE:Factory/rubygem-net-ssh/rubygem-net-ssh.changes 2018-07-18 22:50:36.251624555 +0200 +++ /work/SRC/openSUSE:Factory/.rubygem-net-ssh.new.28833/rubygem-net-ssh.changes 2019-03-04 09:20:27.640600059 +0100 @@ -1,0 +2,18 @@ +Mon Jan 14 13:48:19 UTC 2019 - Stephan Kulow <co...@suse.com> + +- updated to version 5.1.0 + see installed CHANGES.txt + + === 5.1.0.rc1 + + * Support new OpenSSH private key format for rsa - bcrypt for rsa (ed25519 already supported) [#646] + * Support IdentityAgent is ssh config [Frank Groeneveld, #645] + * Improve Match processin in ssh config [Aleksandrs Ļedovskis, #642] + * Ignore signature verification when verify_host_key is never [Piotr Kliczewski, #641] + * Alg preference was changed to prefer stronger encryptions [Tray, #637] + + === 5.0.2 + + * fix ctr for jruby [#612] + +------------------------------------------------------------------- Old: ---- net-ssh-5.0.2.gem New: ---- net-ssh-5.1.0.gem ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ rubygem-net-ssh.spec ++++++ --- /var/tmp/diff_new_pack.PtetDo/_old 2019-03-04 09:20:28.464599911 +0100 +++ /var/tmp/diff_new_pack.PtetDo/_new 2019-03-04 09:20:28.468599910 +0100 @@ -1,7 +1,7 @@ # # spec file for package rubygem-net-ssh # -# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -12,7 +12,7 @@ # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. -# Please submit bugfixes or comments via http://bugs.opensuse.org/ +# Please submit bugfixes or comments via https://bugs.opensuse.org/ # @@ -24,7 +24,7 @@ # Name: rubygem-net-ssh -Version: 5.0.2 +Version: 5.1.0 Release: 0 %define mod_name net-ssh %define mod_full_name %{mod_name}-%{version} ++++++ net-ssh-5.0.2.gem -> net-ssh-5.1.0.gem ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.travis.yml new/.travis.yml --- old/.travis.yml 2018-06-17 06:57:31.000000000 +0200 +++ new/.travis.yml 2018-12-28 11:35:33.000000000 +0100 @@ -7,40 +7,41 @@ gateway.netssh rvm: - - 2.2 + - 2.2.10 - 2.3.7 - - 2.4.4 - - 2.5.1 - - jruby-9.1.13.0 - - rbx-3.84 + - 2.4.5 + - 2.5.3 + - 2.6.0-rc2 + - jruby-9.2.5.0 + - rbx-3.107 - ruby-head env: NET_SSH_RUN_INTEGRATION_TESTS=1 matrix: exclude: - - rvm: rbx-3.84 - - rvm: jruby-9.1.13.0 + - rvm: rbx-3.107 + - rvm: jruby-9.2.5.0 include: - - rvm: rbx-3.84 + - rvm: rbx-3.107 env: NET_SSH_RUN_INTEGRATION_TESTS= - - rvm: jruby-9.1.13.0 + - rvm: jruby-9.2.5.0 env: JRUBY_OPTS='--client -J-XX:+TieredCompilation -J-XX:TieredStopAtLevel=1 -Xcext.enabled=false -J-Xss2m -Xcompile.invokedynamic=false' NET_SSH_RUN_INTEGRATION_TESTS= fast_finish: true allow_failures: - - rvm: rbx-3.84 - - rvm: jruby-9.1.13.0 + - rvm: rbx-3.107 + - rvm: jruby-9.2.5.0 - rvm: ruby-head install: - export JRUBY_OPTS='--client -J-XX:+TieredCompilation -J-XX:TieredStopAtLevel=1 -Xcext.enabled=false -J-Xss2m -Xcompile.invokedynamic=false' - - sudo pip install ansible + - sudo pip install ansible urllib3 pyOpenSSL ndg-httpsclient pyasn1 - gem install bundler -v "= 1.16" - gem list bundler - bundle _1.16_ install - bundle _1.16_ -v - BUNDLE_GEMFILE=./Gemfile.noed25519 bundle _1.16_ install - - sudo ansible-galaxy install rvm_io.ruby + - sudo ansible-galaxy install rvm.ruby - sudo chown -R travis:travis /home/travis/.ansible - ansible-playbook ./test/integration/playbook.yml -i "localhost," --become -c local -e 'no_rvm=true' -e 'myuser=travis' -e 'mygroup=travis' -e 'homedir=/home/travis' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CHANGES.txt new/CHANGES.txt --- old/CHANGES.txt 2018-06-17 06:57:31.000000000 +0200 +++ new/CHANGES.txt 2018-12-28 11:35:33.000000000 +0100 @@ -1,3 +1,15 @@ +=== 5.1.0.rc1 + + * Support new OpenSSH private key format for rsa - bcrypt for rsa (ed25519 already supported) [#646] + * Support IdentityAgent is ssh config [Frank Groeneveld, #645] + * Improve Match processin in ssh config [Aleksandrs Ļedovskis, #642] + * Ignore signature verification when verify_host_key is never [Piotr Kliczewski, #641] + * Alg preference was changed to prefer stronger encryptions [Tray, #637] + +=== 5.0.2 + + * fix ctr for jruby [#612] + === 5.0.1 * default_keys were not loaded even if no keys or key_data options specified [#607] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/README.rdoc new/README.rdoc --- old/README.rdoc 2018-06-17 06:57:31.000000000 +0200 +++ new/README.rdoc 2018-12-28 11:35:33.000000000 +0100 @@ -2,6 +2,9 @@ {<img src="https://badges.gitter.im/net-ssh/net-ssh.svg" alt="Join the chat at https://gitter.im/net-ssh/net-ssh">}[https://gitter.im/net-ssh/net-ssh?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge] {<img src="https://travis-ci.org/net-ssh/net-ssh.svg?branch=master" alt="Build Status" />}[https://travis-ci.org/net-ssh/net-ssh] {<img src="https://codecov.io/gh/net-ssh/net-ssh/branch/master/graph/badge.svg" alt="Coverage status" />}[https://codecov.io/gh/net-ssh/net-ssh] +{<img src="https://opencollective.com/net-ssh/backers/badge.svg" alt="Backers on Open Collective" />}[#backers] +{<img src="https://opencollective.com/net-ssh/sponsors/badge.svg" alt="Sponsors on Open Collective" />}[#sponsors] + = Net::SSH 5.x @@ -142,6 +145,28 @@ mv gem-public_cert.pem net-ssh-public_cert.pem gem cert --add net-ssh-public_cert.pem +== CREDITS + +=== Contributors + +This project exists thanks to all the people who contribute. + +{<img src="https://opencollective.com/net-ssh/contributors.svg?width=890&button=false" />}["graphs/contributors"] + + +=== Backers + +Thank you to all our backers! 🙏 {Become a backer}[https://opencollective.com/net-ssh#backer)] + +{<img src="https://opencollective.com/net-ssh/backers.svg?width=890”>}["https://opencollective.com/net-ssh#backers"] + +=== Sponsors + +Support this project by becoming a sponsor. Your logo will show up here with a link to your website. {Become a sponsor}[https://opencollective.com/net-ssh#sponsor] +{<img src="https://opencollective.com/net-ssh/sponsor/0/avatar.svg" alt="Sponsor" />}[https://opencollective.com/net-ssh/sponsor/0/website] + + + == LICENSE: Binary files old/checksums.yaml.gz and new/checksums.yaml.gz differ Binary files old/checksums.yaml.gz.sig and new/checksums.yaml.gz.sig differ Binary files old/data.tar.gz.sig and new/data.tar.gz.sig differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/net/ssh/authentication/agent.rb new/lib/net/ssh/authentication/agent.rb --- old/lib/net/ssh/authentication/agent.rb 2018-06-17 06:57:31.000000000 +0200 +++ new/lib/net/ssh/authentication/agent.rb 2018-12-28 11:35:33.000000000 +0100 @@ -62,9 +62,9 @@ # Instantiates a new agent object, connects to a running SSH agent, # negotiates the agent protocol version, and returns the agent object. - def self.connect(logger=nil, agent_socket_factory = nil) + def self.connect(logger=nil, agent_socket_factory = nil, identity_agent = nil) agent = new(logger) - agent.connect!(agent_socket_factory) + agent.connect!(agent_socket_factory, identity_agent) agent.negotiate! agent end @@ -79,11 +79,13 @@ # given by the attribute writers. If the agent on the other end of the # socket reports that it is an SSH2-compatible agent, this will fail # (it only supports the ssh-agent distributed by OpenSSH). - def connect!(agent_socket_factory = nil) + def connect!(agent_socket_factory = nil, identity_agent = nil) debug { "connecting to ssh-agent" } @socket = if agent_socket_factory agent_socket_factory.call + elsif identity_agent + unix_socket_class.open(identity_agent) elsif ENV['SSH_AUTH_SOCK'] && unix_socket_class unix_socket_class.open(ENV['SSH_AUTH_SOCK']) elsif Gem.win_platform? && RUBY_ENGINE != "jruby" @@ -124,6 +126,10 @@ comment_str = body.read_string begin key = Buffer.new(key_str).read_key + if key.nil? + error { "ignoring invalid key: #{comment_str}" } + next + end key.extend(Comment) key.comment = comment_str identities.push key diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/net/ssh/authentication/ed25519.rb new/lib/net/ssh/authentication/ed25519.rb --- old/lib/net/ssh/authentication/ed25519.rb 2018-06-17 06:57:31.000000000 +0200 +++ new/lib/net/ssh/authentication/ed25519.rb 2018-12-28 11:35:33.000000000 +0100 @@ -22,51 +22,14 @@ end end - class PubKey - include Net::SSH::Authentication::PubKeyFingerprint - - attr_reader :verify_key - - def initialize(data) - @verify_key = ::Ed25519::VerifyKey.new(data) - end - - def self.read_keyblob(buffer) - PubKey.new(buffer.read_string) - end - - def to_blob - Net::SSH::Buffer.from(:mstring,"ssh-ed25519",:string,@verify_key.to_bytes).to_s - end - - def ssh_type - "ssh-ed25519" - end - - def ssh_signature_type - ssh_type - end - - def ssh_do_verify(sig,data) - @verify_key.verify(sig,data) - end - - def to_pem - # TODO this is not pem - ssh_type + Base64.encode64(@verify_key.to_bytes) - end - end - - class PrivKey + class OpenSSHPrivateKeyLoader CipherFactory = Net::SSH::Transport::CipherFactory MBEGIN = "-----BEGIN OPENSSH PRIVATE KEY-----\n" MEND = "-----END OPENSSH PRIVATE KEY-----\n" MAGIC = "openssh-key-v1" - attr_reader :sign_key - - def initialize(datafull,password) + def self.read(datafull, password) raise ArgumentError.new("Expected #{MBEGIN} at start of private key") unless datafull.start_with?(MBEGIN) raise ArgumentError.new("Expected #{MEND} at end of private key") unless datafull.end_with?(MEND) datab64 = datafull[MBEGIN.size...-MEND.size] @@ -113,10 +76,64 @@ raise ArgumentError, "Decrypt failed on private key" if (check1 != check2) - _type_name = decoded.read_string - pk = decoded.read_string - sk = decoded.read_string - _comment = decoded.read_string + type_name = decoded.read_string + case type_name + when "ssh-ed25519" + PrivKey.new(decoded) + else + decoded.read_private_keyblob(type_name) + end + end + end + + class PubKey + include Net::SSH::Authentication::PubKeyFingerprint + + attr_reader :verify_key + + def initialize(data) + @verify_key = ::Ed25519::VerifyKey.new(data) + end + + def self.read_keyblob(buffer) + PubKey.new(buffer.read_string) + end + + def to_blob + Net::SSH::Buffer.from(:mstring,"ssh-ed25519",:string,@verify_key.to_bytes).to_s + end + + def ssh_type + "ssh-ed25519" + end + + def ssh_signature_type + ssh_type + end + + def ssh_do_verify(sig,data) + @verify_key.verify(sig,data) + end + + def to_pem + # TODO this is not pem + ssh_type + Base64.encode64(@verify_key.to_bytes) + end + end + + class PrivKey + CipherFactory = Net::SSH::Transport::CipherFactory + + MBEGIN = "-----BEGIN OPENSSH PRIVATE KEY-----\n" + MEND = "-----END OPENSSH PRIVATE KEY-----\n" + MAGIC = "openssh-key-v1" + + attr_reader :sign_key + + def initialize(buffer) + pk = buffer.read_string + sk = buffer.read_string + _comment = buffer.read_string @pk = pk @sign_key = SigningKeyFromFile.new(pk,sk) @@ -142,8 +159,8 @@ @sign_key.sign(data) end - def self.read(data,password) - self.new(data,password) + def self.read(data, password) + OpenSSHPrivateKeyLoader.read(data, password) end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/net/ssh/authentication/key_manager.rb new/lib/net/ssh/authentication/key_manager.rb --- old/lib/net/ssh/authentication/key_manager.rb 2018-06-17 06:57:31.000000000 +0200 +++ new/lib/net/ssh/authentication/key_manager.rb 2018-12-28 11:35:33.000000000 +0100 @@ -176,7 +176,7 @@ # or if the agent is otherwise not available. def agent return unless use_agent? - @agent ||= Agent.connect(logger, options[:agent_socket_factory]) + @agent ||= Agent.connect(logger, options[:agent_socket_factory], options[:identity_agent]) rescue AgentNotAvailable @use_agent = false nil diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/net/ssh/authentication/methods/publickey.rb new/lib/net/ssh/authentication/methods/publickey.rb --- old/lib/net/ssh/authentication/methods/publickey.rb 2018-06-17 06:57:31.000000000 +0200 +++ new/lib/net/ssh/authentication/methods/publickey.rb 2018-12-28 11:35:33.000000000 +0100 @@ -82,6 +82,8 @@ when USERAUTH_FAILURE return false + when USERAUTH_SUCCESS + return true else raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/net/ssh/buffer.rb new/lib/net/ssh/buffer.rb --- old/lib/net/ssh/buffer.rb 2018-06-17 06:57:31.000000000 +0200 +++ new/lib/net/ssh/buffer.rb 2018-12-28 11:35:33.000000000 +0100 @@ -249,6 +249,46 @@ return (type ? read_keyblob(type) : nil) end + def read_private_keyblob(type) + case type + when /^ssh-rsa$/ + key = OpenSSL::PKey::RSA.new + n = read_bignum + e = read_bignum + d = read_bignum + iqmp = read_bignum + p = read_bignum + q = read_bignum + _unkown1 = read_bignum + _unkown2 = read_bignum + dmp1 = d % (p - 1) + dmq1 = d % (q - 1) + if key.respond_to?(:set_key) + key.set_key(n, e, d) + else + key.e = e + key.n = n + key.d = d + end + if key.respond_to?(:set_factors) + key.set_factors(p, q) + else + key.p = p + key.q = q + end + if key.respond_to?(:set_crt_params) + key.set_crt_params(dmp1, dmq1, iqmp) + else + key.dmp1 = dmp1 + key.dmq1 = dmq1 + key.iqmp = iqmp + end + key + else + raise Exception, "Cannot decode private key of type #{type}" + end + end + # Read a keyblob of the given type from the buffer, and return it as # a key. Only RSA, DSA, and ECDSA keys are supported. def read_keyblob(type) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/net/ssh/config.rb new/lib/net/ssh/config.rb --- old/lib/net/ssh/config.rb 2018-06-17 06:57:31.000000000 +0200 +++ new/lib/net/ssh/config.rb 2018-12-28 11:35:33.000000000 +0100 @@ -22,6 +22,7 @@ # * HostKeyAlias => :host_key_alias # * HostName => :host_name # * IdentityFile => maps to the :keys option + # * IdentityAgent => :identity_agent # * IdentitiesOnly => :keys_only # * Macs => maps to the :hmac option # * PasswordAuthentication => maps to the :auth_methods option password @@ -95,7 +96,7 @@ next if value.nil? key.downcase! - value = $1 if value =~ /^"(.*)"$/ + value = unquote(value) value = case value.strip when /^\d+$/ then value.to_i @@ -194,6 +195,7 @@ connecttimeout: :timeout, forwardagent: :forward_agent, identitiesonly: :keys_only, + identityagent: :identity_agent, globalknownhostsfile: :global_known_hosts_file, hostkeyalias: :host_key_alias, identityfile: :keys, @@ -326,12 +328,17 @@ end def eval_match_conditions(condition, host, settings) - conditions = condition.split(/\s+/) + # Not using `\s` for whitespace matching as canonical + # ssh_config parser implementation (OpenSSH) has specific character set. + # Ref: https://github.com/openssh/openssh-portable/blob/2581333d564d8697837729b3d07d45738eaf5a54/misc.c#L237-L239 + conditions = condition.split(/[ \t\r\n]+|(?<!=)=(?!=)/).reject(&:empty?) return true if conditions == ["all"] conditions = conditions.each_slice(2) - matching = true + condition_matches = [] conditions.each do |(kind,exprs)| + exprs = unquote(exprs) + case kind.downcase when "all" raise "all cannot be mixed with other conditions" @@ -346,12 +353,17 @@ exprs.split(",").each do |expr| condition_met = condition_met || host =~ pattern2regex(expr) end - matching = matching && negated ^ condition_met + condition_matches << (true && negated ^ condition_met) # else # warn "net-ssh: Unsupported expr in Match block: #{kind}" end end - matching + + !condition_matches.empty? && condition_matches.all? + end + + def unquote(string) + string =~ /^"(.*)"$/ ? Regexp.last_match(1) : string end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/net/ssh/key_factory.rb new/lib/net/ssh/key_factory.rb --- old/lib/net/ssh/key_factory.rb 2018-06-17 06:57:31.000000000 +0200 +++ new/lib/net/ssh/key_factory.rb 2018-12-28 11:35:33.000000000 +0100 @@ -111,7 +111,7 @@ def classify_key(data, filename) if data.match(/-----BEGIN OPENSSH PRIVATE KEY-----/) Net::SSH::Authentication::ED25519Loader.raiseUnlessLoaded("OpenSSH keys only supported if ED25519 is available") - return ->(key_data, passphrase) { Net::SSH::Authentication::ED25519::PrivKey.read(key_data, passphrase) }, [ArgumentError] + return ->(key_data, passphrase) { Net::SSH::Authentication::ED25519::OpenSSHPrivateKeyLoader.read(key_data, passphrase) }, [ArgumentError] elsif OpenSSL::PKey.respond_to?(:read) return ->(key_data, passphrase) { OpenSSL::PKey.read(key_data, passphrase) }, [ArgumentError, OpenSSL::PKey::PKeyError] elsif data.match(/-----BEGIN DSA PRIVATE KEY-----/) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/net/ssh/test/extensions.rb new/lib/net/ssh/test/extensions.rb --- old/lib/net/ssh/test/extensions.rb 2018-06-17 06:57:31.000000000 +0200 +++ new/lib/net/ssh/test/extensions.rb 2018-12-28 11:35:33.000000000 +0100 @@ -156,6 +156,8 @@ end raise "no readers were ready for reading, and none had any incoming packets" if processed == 0 && wait != 0 + + [[], [], []] end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/net/ssh/test.rb new/lib/net/ssh/test.rb --- old/lib/net/ssh/test.rb 2018-06-17 06:57:31.000000000 +0200 +++ new/lib/net/ssh/test.rb 2018-12-28 11:35:33.000000000 +0100 @@ -3,7 +3,7 @@ require 'net/ssh/test/kex' require 'net/ssh/test/socket' -module Net +module Net module SSH # This module may be used in unit tests, for when you want to test that your @@ -54,30 +54,30 @@ Net::SSH::Test::Extensions::IO.with_test_extension { yield socket.script if block_given? } return socket.script end - + # Returns the test socket instance to use for these tests (see # Net::SSH::Test::Socket). def socket(options={}) @socket ||= Net::SSH::Test::Socket.new end - + # Returns the connection session (Net::SSH::Connection::Session) for use # in these tests. It is a fully functional SSH session, operating over # a mock socket (#socket). def connection(options={}) @connection ||= Net::SSH::Connection::Session.new(transport(options), options) end - + # Returns the transport session (Net::SSH::Transport::Session) for use # in these tests. It is a fully functional SSH transport session, operating # over a mock socket (#socket). def transport(options={}) @transport ||= Net::SSH::Transport::Session.new( options[:host] || "localhost", - options.merge(kex: "test", host_key: "ssh-rsa", verify_host_key: false, proxy: socket(options)) + options.merge(kex: "test", host_key: "ssh-rsa", verify_host_key: :never, proxy: socket(options)) ) end - + # First asserts that a story has been described (see #story). Then yields, # and then asserts that all items described in the script have been # processed. Typically, this is called immediately after a story has diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/net/ssh/transport/algorithms.rb new/lib/net/ssh/transport/algorithms.rb --- old/lib/net/ssh/transport/algorithms.rb 2018-06-17 06:57:31.000000000 +0200 +++ new/lib/net/ssh/transport/algorithms.rb 2018-12-28 11:35:33.000000000 +0100 @@ -8,8 +8,8 @@ require 'net/ssh/transport/server_version' require 'net/ssh/authentication/ed25519_loader' -module Net - module SSH +module Net + module SSH module Transport # Implements the higher-level logic behind an SSH key-exchange. It handles @@ -22,92 +22,109 @@ class Algorithms include Loggable include Constants - + # Define the default algorithms, in order of preference, supported by # Net::SSH. ALGORITHMS = { - host_key: %w[ssh-rsa ssh-dss - ssh-rsa-cert-...@openssh.com - ssh-rsa-cert-...@openssh.com], - kex: %w[diffie-hellman-group-exchange-sha1 - diffie-hellman-group1-sha1 + host_key: %w[ssh-rsa-cert-...@openssh.com + ssh-rsa-cert-...@openssh.com + ssh-rsa ssh-dss], + kex: %w[diffie-hellman-group-exchange-sha256 + diffie-hellman-group-exchange-sha1 diffie-hellman-group14-sha1 - diffie-hellman-group-exchange-sha256], - encryption: %w[aes128-cbc 3des-cbc blowfish-cbc cast128-cbc - aes192-cbc aes256-cbc rijndael-...@lysator.liu.se - idea-cbc arcfour128 arcfour256 arcfour - aes128-ctr aes192-ctr aes256-ctr - cast128-ctr blowfish-ctr 3des-ctr none], - - hmac: %w[hmac-sha1 hmac-md5 hmac-sha1-96 hmac-md5-96 + diffie-hellman-group1-sha1], + encryption: %w[aes256-ctr aes192-ctr aes128-ctr + aes256-cbc aes192-cbc aes128-cbc + rijndael-...@lysator.liu.se + blowfish-ctr blowfish-cbc + cast128-ctr cast128-cbc + 3des-ctr 3des-cbc + idea-cbc arcfour256 arcfour128 arcfour + none], + + hmac: %w[hmac-sha2-512 hmac-sha2-256 + hmac-sha2-512-96 hmac-sha2-256-96 + hmac-sha1 hmac-sha1-96 hmac-ripemd160 hmac-ripemd...@openssh.com - hmac-sha2-256 hmac-sha2-512 hmac-sha2-256-96 - hmac-sha2-512-96 none], - + hmac-md5 hmac-md5-96 + none], + compression: %w[none z...@openssh.com zlib], language: %w[] } if defined?(OpenSSL::PKey::EC) - ALGORITHMS[:host_key] += %w[ecdsa-sha2-nistp256 - ecdsa-sha2-nistp384 - ecdsa-sha2-nistp521] - ALGORITHMS[:host_key] += %w[ssh-ed25519] if Net::SSH::Authentication::ED25519Loader::LOADED - ALGORITHMS[:kex] += %w[ecdh-sha2-nistp256 - ecdh-sha2-nistp384 - ecdh-sha2-nistp521] + ALGORITHMS[:host_key].unshift( + "ecdsa-sha2-nistp521-cert-...@openssh.com", + "ecdsa-sha2-nistp384-cert-...@openssh.com", + "ecdsa-sha2-nistp256-cert-...@openssh.com", + "ecdsa-sha2-nistp521", + "ecdsa-sha2-nistp384", + "ecdsa-sha2-nistp256" + ) + if Net::SSH::Authentication::ED25519Loader::LOADED + ALGORITHMS[:host_key].unshift( + "ssh-ed25519-cert-...@openssh.com", + "ssh-ed25519" + ) + end + ALGORITHMS[:kex].unshift( + "ecdh-sha2-nistp521", + "ecdh-sha2-nistp384", + "ecdh-sha2-nistp256" + ) end - + # The underlying transport layer session that supports this object attr_reader :session - + # The hash of options used to initialize this object attr_reader :options - + # The kex algorithm to use settled on between the client and server. attr_reader :kex - + # The type of host key that will be used for this session. attr_reader :host_key - + # The type of the cipher to use to encrypt packets sent from the client to # the server. attr_reader :encryption_client - + # The type of the cipher to use to decrypt packets arriving from the server. attr_reader :encryption_server - + # The type of HMAC to use to sign packets sent by the client. attr_reader :hmac_client - + # The type of HMAC to use to validate packets arriving from the server. attr_reader :hmac_server - + # The type of compression to use to compress packets being sent by the client. attr_reader :compression_client - + # The type of compression to use to decompress packets arriving from the server. attr_reader :compression_server - + # The language that will be used in messages sent by the client. attr_reader :language_client - + # The language that will be used in messages sent from the server. attr_reader :language_server - + # The hash of algorithms preferred by the client, which will be told to # the server during algorithm negotiation. attr_reader :algorithms - + # The session-id for this session, as decided during the initial key exchange. attr_reader :session_id - + # Returns true if the given packet can be processed during a key-exchange. def self.allowed_packet?(packet) (1..4).include?(packet.type) || (6..19).include?(packet.type) || (21..49).include?(packet.type) end - + # Instantiates a new Algorithms object, and prepares the hash of preferred # algorithms based on the options parameter and the ALGORITHMS constant. def initialize(session, options={}) @@ -119,13 +136,13 @@ @client_packet = @server_packet = nil prepare_preferred_algorithms! end - + # Start the algorithm negotation def start raise ArgumentError, "Cannot call start if it's negotiation started or done" if @pending || @initialized send_kexinit end - + # Request a rekey operation. This will return immediately, and does not # actually perform the rekey operation. It does cause the session to change # state, however--until the key exchange finishes, no new packets will be @@ -135,7 +152,7 @@ @initialized = false send_kexinit end - + # Called by the transport layer when a KEXINIT packet is received, indicating # that the server wants to exchange keys. This can be spontaneous, or it # can be in response to a client-initiated rekey request (see #rekey!). Either @@ -150,13 +167,13 @@ proceed! end end - + # A convenience method for accessing the list of preferred types for a # specific algorithm (see #algorithms). def [](key) algorithms[key] end - + # Returns +true+ if a key-exchange is pending. This will be true from the # moment either the client or server requests the key exchange, until the # exchange completes. While an exchange is pending, only a limited number @@ -201,7 +218,7 @@ session.send_message(packet) proceed! if @server_packet end - + # After both client and server have sent their KEXINIT packets, this # will do the algorithm negotiation and key exchange. Once both finish, # the object leaves the pending state and the method returns. @@ -211,7 +228,7 @@ exchange_keys @pending = false end - + # Prepares the list of preferred algorithms, based on the options hash # that was given when the object was constructed, and the ALGORITHMS # constant. Also, when determining the host_key type to use, the known @@ -220,23 +237,23 @@ # communicating with this server. def prepare_preferred_algorithms! options[:compression] = %w[z...@openssh.com zlib] if options[:compression] == true - + ALGORITHMS.each do |algorithm, supported| algorithms[algorithm] = compose_algorithm_list(supported, options[algorithm], options[:append_all_supported_algorithms]) end - + # for convention, make sure our list has the same keys as the server # list - + algorithms[:encryption_client ] = algorithms[:encryption_server ] = algorithms[:encryption] algorithms[:hmac_client ] = algorithms[:hmac_server ] = algorithms[:hmac] algorithms[:compression_client] = algorithms[:compression_server] = algorithms[:compression] algorithms[:language_client ] = algorithms[:language_server ] = algorithms[:language] - + if !options.key?(:host_key) # make sure the host keys are specified in preference order, where any # existing known key for the host has preference. - + existing_keys = session.host_keys host_keys = existing_keys.map { |key| key.ssh_type }.uniq algorithms[:host_key].each do |name| @@ -245,14 +262,14 @@ algorithms[:host_key] = host_keys end end - + # Composes the list of algorithms by taking supported algorithms and matching with supplied options. def compose_algorithm_list(supported, option, append_all_supported_algorithms = false) return supported.dup unless option - + list = [] option = Array(option).compact.uniq - + if option.first && option.first.start_with?('+') list = supported.dup list << option.first[1..-1] @@ -260,30 +277,30 @@ list.uniq! else list = option - + if append_all_supported_algorithms supported.each { |name| list << name unless list.include?(name) } end end - + unsupported = [] list.select! do |name| is_supported = supported.include?(name) unsupported << name unless is_supported is_supported end - + lwarn { %(unsupported algorithm: `#{unsupported}') } unless unsupported.empty? - + list end - + # Parses a KEXINIT packet from the server. def parse_server_algorithm_packet(packet) data = { raw: packet.content } - + packet.read(16) # skip the cookie value - + data[:kex] = packet.read_string.split(/,/) data[:host_key] = packet.read_string.split(/,/) data[:encryption_client] = packet.read_string.split(/,/) @@ -294,15 +311,15 @@ data[:compression_server] = packet.read_string.split(/,/) data[:language_client] = packet.read_string.split(/,/) data[:language_server] = packet.read_string.split(/,/) - + # TODO: if first_kex_packet_follows, we need to try to skip the # actual kexinit stuff and try to guess what the server is doing... # need to read more about this scenario. # first_kex_packet_follows = packet.read_bool - + return data end - + # Given the #algorithms map of preferred algorithm types, this constructs # a KEXINIT packet to send to the server. It does not actually send it, # it simply builds the packet and returns it. @@ -313,14 +330,14 @@ hmac = algorithms[:hmac].join(",") compression = algorithms[:compression].join(",") language = algorithms[:language].join(",") - + Net::SSH::Buffer.from(:byte, KEXINIT, :long, [rand(0xFFFFFFFF), rand(0xFFFFFFFF), rand(0xFFFFFFFF), rand(0xFFFFFFFF)], :mstring, [kex, host_key, encryption, encryption, hmac, hmac], :mstring, [compression, compression, language, language], :bool, false, :long, 0) end - + # Given the parsed server KEX packet, and the client's preferred algorithm # lists in #algorithms, determine which preferred algorithms each has # in common and set those as the selected algorithms. If, for any algorithm, @@ -336,7 +353,7 @@ @compression_server = negotiate(:compression_server) @language_client = negotiate(:language_client) rescue "" @language_server = negotiate(:language_server) rescue "" - + debug do "negotiated:\n" + %i[kex host_key encryption_server encryption_client hmac_client hmac_server compression_client compression_server language_client language_server].map do |key| @@ -344,40 +361,40 @@ end.join("\n") end end - + # Negotiates a single algorithm based on the preferences reported by the # server and those set by the client. This is called by # #negotiate_algorithms. def negotiate(algorithm) match = self[algorithm].find { |item| @server_data[algorithm].include?(item) } - + raise Net::SSH::Exception, "could not settle on #{algorithm} algorithm" if match.nil? - + return match end - + # Considers the sizes of the keys and block-sizes for the selected ciphers, # and the lengths of the hmacs, and returns the largest as the byte requirement # for the key-exchange algorithm. def kex_byte_requirement sizes = [8] # require at least 8 bytes - + sizes.concat(CipherFactory.get_lengths(encryption_client)) sizes.concat(CipherFactory.get_lengths(encryption_server)) - + sizes << HMAC.key_length(hmac_client) sizes << HMAC.key_length(hmac_server) - + sizes.max end - + # Instantiates one of the Transport::Kex classes (based on the negotiated # kex algorithm), and uses it to exchange keys. Then, the ciphers and # HMACs are initialized and fed to the transport layer, to be used in # further communication with the server. def exchange_keys debug { "exchanging keys" } - + algorithm = Kex::MAP[kex].new(self, session, client_version_string: Net::SSH::Transport::ServerVersion::PROTO_VERSION, server_version_string: session.server_version.version, @@ -387,46 +404,46 @@ minimum_dh_bits: options[:minimum_dh_bits], logger: logger) result = algorithm.exchange_keys - + secret = result[:shared_secret].to_ssh hash = result[:session_id] digester = result[:hashing_algorithm] - + @session_id ||= hash - + key = Proc.new { |salt| digester.digest(secret + hash + salt + @session_id) } - + iv_client = key["A"] iv_server = key["B"] key_client = key["C"] key_server = key["D"] mac_key_client = key["E"] mac_key_server = key["F"] - + parameters = { shared: secret, hash: hash, digester: digester } - + cipher_client = CipherFactory.get(encryption_client, parameters.merge(iv: iv_client, key: key_client, encrypt: true)) cipher_server = CipherFactory.get(encryption_server, parameters.merge(iv: iv_server, key: key_server, decrypt: true)) - + mac_client = HMAC.get(hmac_client, mac_key_client, parameters) mac_server = HMAC.get(hmac_server, mac_key_server, parameters) - + session.configure_client cipher: cipher_client, hmac: mac_client, compression: normalize_compression_name(compression_client), compression_level: options[:compression_level], rekey_limit: options[:rekey_limit], max_packets: options[:rekey_packet_limit], max_blocks: options[:rekey_blocks_limit] - + session.configure_server cipher: cipher_server, hmac: mac_server, compression: normalize_compression_name(compression_server), rekey_limit: options[:rekey_limit], max_packets: options[:rekey_packet_limit], max_blocks: options[:rekey_blocks_limit] - + @initialized = true end - + # Given the SSH name for some compression algorithm, return a normalized # name as a symbol. def normalize_compression_name(name) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb new/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb --- old/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb 2018-06-17 06:57:31.000000000 +0200 +++ new/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb 2018-12-28 11:35:33.000000000 +0100 @@ -206,7 +206,7 @@ hash = @digester.digest(response.to_s) - raise Net::SSH::Exception, "could not verify server signature" unless result[:server_key].ssh_do_verify(result[:server_sig], hash) + raise Net::SSH::Exception, "could not verify server signature" unless connection.host_key_verifier.verify_signature { result[:server_key].ssh_do_verify(result[:server_sig], hash) } return hash end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/net/ssh/transport/openssl.rb new/lib/net/ssh/transport/openssl.rb --- old/lib/net/ssh/transport/openssl.rb 2018-06-17 06:57:31.000000000 +0200 +++ new/lib/net/ssh/transport/openssl.rb 2018-12-28 11:35:33.000000000 +0100 @@ -225,6 +225,22 @@ return Net::SSH::Buffer.from(:bignum, sig_r, :bignum, sig_s).to_s end + + class Point + # Returns the description of this key type used by the + # SSH2 protocol, like "ecdsa-sha2-nistp256" + def ssh_type + "ecdsa-sha2-#{CurveNameAliasInv[self.group.curve_name]}" + end + + # Converts the key to a blob, according to the SSH2 protocol. + def to_blob + @blob ||= Net::SSH::Buffer.from(:string, ssh_type, + :string, CurveNameAliasInv[self.group.curve_name], + :mstring, self.to_bn.to_s(2)).to_s + @blob + end + end end else class OpenSSL::PKey::ECError < RuntimeError diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/net/ssh/transport/packet_stream.rb new/lib/net/ssh/transport/packet_stream.rb --- old/lib/net/ssh/transport/packet_stream.rb 2018-06-17 06:57:31.000000000 +0200 +++ new/lib/net/ssh/transport/packet_stream.rb 2018-12-28 11:35:33.000000000 +0100 @@ -81,8 +81,8 @@ # 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 # returned. If the mode parameter is :block, then this method will block - # until a packet is available. - def next_packet(mode=:nonblock) + # until a packet is available or timeout seconds have passed. + def next_packet(mode=:nonblock, timeout=nil) case mode when :nonblock then packet = poll_next_packet @@ -105,11 +105,8 @@ packet = poll_next_packet return packet if packet - loop do - result = IO.select([self]) or next - break if result.first.any? - end - + result = IO.select([self], nil, nil, timeout) + raise Net::SSH::ConnectionTimeout, "timeout waiting for next packet" unless result raise Net::SSH::Disconnect, "connection closed by remote host" if fill <= 0 end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/net/ssh/transport/session.rb new/lib/net/ssh/transport/session.rb --- old/lib/net/ssh/transport/session.rb 2018-06-17 06:57:31.000000000 +0200 +++ new/lib/net/ssh/transport/session.rb 2018-12-28 11:35:33.000000000 +0100 @@ -190,7 +190,7 @@ loop do return @queue.shift if consume_queue && @queue.any? && algorithms.allow?(@queue.first) - packet = socket.next_packet(mode) + packet = socket.next_packet(mode, options[:timeout]) return nil if packet.nil? case packet.type @@ -274,6 +274,23 @@ private + # Compatibility verifier which allows users to keep using + # custom verifier code without adding new :verify_signature + # method. + class CompatibleVerifier + def initialize(verifier) + @verifier + end + + def verify(arguments) + @verifier.verify(arguments) + end + + def verify_signature(&block) + yield + end + end + # Instantiates a new host-key verification class, based on the value of # the parameter. # @@ -285,8 +302,8 @@ # - :accept_new (insecure) # - :always (secure) # - # If the argument happens to respond to :verify, it is returned - # directly. Otherwise, an exception is raised. + # If the argument happens to respond to :verify and :verify_signature, + # it is returned directly. Otherwise, an exception is raised. # # Values false, true, and :very were deprecated in # [#595](https://github.com/net-ssh/net-ssh/pull/595) @@ -314,7 +331,12 @@ Net::SSH::Verifiers::Always.new else if verifier.respond_to?(:verify) - verifier + if verifier.respond_to?(:verify_signature) + verifier + else + Kernel.warn("Warning: verifier without :verify_signature is deprecated") + CompatibleVerifier.new(verifier) + end else raise( ArgumentError, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/net/ssh/verifiers/accept_new.rb new/lib/net/ssh/verifiers/accept_new.rb --- old/lib/net/ssh/verifiers/accept_new.rb 2018-06-17 06:57:31.000000000 +0200 +++ new/lib/net/ssh/verifiers/accept_new.rb 2018-12-28 11:35:33.000000000 +0100 @@ -21,6 +21,13 @@ return true end end + + def verify_signature(&block) + yield + rescue HostKeyUnknown => err + err.remember_host! + return true + end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/net/ssh/verifiers/always.rb new/lib/net/ssh/verifiers/always.rb --- old/lib/net/ssh/verifiers/always.rb 2018-06-17 06:57:31.000000000 +0200 +++ new/lib/net/ssh/verifiers/always.rb 2018-12-28 11:35:33.000000000 +0100 @@ -34,6 +34,10 @@ found end + def verify_signature(&block) + yield + end + private def process_cache_miss(host_keys, args, exc_class, message) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/net/ssh/verifiers/never.rb new/lib/net/ssh/verifiers/never.rb --- old/lib/net/ssh/verifiers/never.rb 2018-06-17 06:57:31.000000000 +0200 +++ new/lib/net/ssh/verifiers/never.rb 2018-12-28 11:35:33.000000000 +0100 @@ -10,6 +10,10 @@ def verify(arguments) true end + + def verify_signature(&block) + true + end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/net/ssh/version.rb new/lib/net/ssh/version.rb --- old/lib/net/ssh/version.rb 2018-06-17 06:57:31.000000000 +0200 +++ new/lib/net/ssh/version.rb 2018-12-28 11:35:33.000000000 +0100 @@ -49,10 +49,10 @@ MAJOR = 5 # The minor component of this version of the Net::SSH library - MINOR = 0 + MINOR = 1 # The tiny component of this version of the Net::SSH library - TINY = 2 + TINY = 0 # The prerelease component of this version of the Net::SSH library # nil allowed diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/metadata new/metadata --- old/metadata 2018-06-17 06:57:31.000000000 +0200 +++ new/metadata 2018-12-28 11:35:33.000000000 +0100 @@ -1,7 +1,7 @@ --- !ruby/object:Gem::Specification name: net-ssh version: !ruby/object:Gem::Version - version: 5.0.2 + version: 5.1.0 platform: ruby authors: - Jamis Buck @@ -32,7 +32,7 @@ ZFwoIuXKeDmTTpryd/vI7sdLXDuV6MbWOLGh6gXn9RDDXG1EqEXW0bjovATBMpdH 9OGohJvAFzcvhDTWPwT6w3PG5B80pqb9j1hEAg== -----END CERTIFICATE----- -date: 2018-06-17 00:00:00.000000000 Z +date: 2018-12-28 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: bcrypt_pbkdf @@ -267,7 +267,7 @@ version: '0' requirements: [] rubyforge_project: -rubygems_version: 2.7.6 +rubygems_version: 2.6.8 signing_key: specification_version: 4 summary: 'Net::SSH: a pure-Ruby implementation of the SSH2 client protocol.' Binary files old/metadata.gz.sig and new/metadata.gz.sig differ