diff --git a/lib/vagrant/patches/net-ssh.rb b/lib/vagrant/patches/net-ssh.rb index a5d65a23a..443f7122a 100644 --- a/lib/vagrant/patches/net-ssh.rb +++ b/lib/vagrant/patches/net-ssh.rb @@ -4,32 +4,122 @@ require "net/ssh" # these patches pull 6.1.0 up to the as of now # current 6.2.0 beta if Net::SSH::Version::STRING == "6.1.0" - require "net/ssh/authentication/methods/publickey" - Net::SSH::Authentication::Methods::Publickey.class_eval do - def rsa_compat_build_request(pub_key, *args) - s_ver_str = session.transport.server_version.version.match(/OpenSSH_(?\d+\.\d+)/)[:version] - begin - s_ver = Gem::Version.new(s_ver_str) - if s_ver >= Gem::Version.new("7.2") && pub_key.is_a?(OpenSSL::PKey::RSA) - pub_key.deprecated_ssh_rsa = true - debug { "public key has been marked for deprecated ssh-rsa SHA1 behavior" } - info = key_manager.known_identities[pub_key] - if info && info[:key] - info[:key].deprecated_ssh_rsa = true - debug { "private key has been marked for deprecated ssh-rsa SHA1 behavior" } - else - warn { "cannot deprecate ssh rsa on private key, not loaded (#{info[:file]})" } + module DeprecatedRsaSha1 + module KeyManager + def initialize(logger, options={}) + @deprecated_rsa_sha1 = options.delete(:deprecated_rsa_sha1) + super + end + + def sign(identity, data) + info = known_identities[identity] or raise Net::SSH::Authentication::KeyManager::KeyManagerError, "the given identity is unknown to the key manager" + + if info[:key].nil? && info[:from] == :file + begin + info[:key] = Net::SSH::KeyFactory.load_private_key(info[:file], options[:passphrase], !options[:non_interactive], options[:password_prompt]) + if @deprecated_rsa_sha1 && info[:key].respond_to?(:deprecated_rsa_sha1=) + info[:key].deprecated_rsa_sha1 = true + Vagrant.global_logger.debug("set RSA SHA1 deprecation on private key: #{info[:key].fingerprint}") + end + rescue OpenSSL::OpenSSLError, Exception => e + raise Net::SSH::Authentication::KeyManager::KeyManagerError, "the given identity is known, but the private key could not be loaded: #{e.class} (#{e.message})" end end - rescue ArgumentError - warn { "failed to parse OpenSSH version (raw: #{session.transport.server_version.version} attempted: #{s_ver_str}" } + + if info[:key] + return Net::SSH::Buffer.from(:string, identity.ssh_signature_type, + :mstring, info[:key].ssh_do_sign(data.to_s)).to_s + end + + if info[:from] == :agent + raise Net::SSH::Authentication::KeyManager::KeyManagerError, "the agent is no longer available" unless agent + return agent.sign(info[:identity], data.to_s) + end + + raise Net::SSH::Authentication::KeyManager::KeyManagerError, "[BUG] can't determine identity origin (#{info.inspect})" + end + + def load_identities(identities, ask_passphrase, ignore_decryption_errors) + identities.map do |identity| + begin + case identity[:load_from] + when :pubkey_file + key = Net::SSH::KeyFactory.load_public_key(identity[:pubkey_file]) + if @deprecated_rsa_sha1 && key.respond_to?(:deprecated_rsa_sha1=) + key.deprecated_rsa_sha1 = true + Vagrant.global_logger.debug("set RSA SHA1 deprecation on public key: #{key.fingerprint}") + end + { public_key: key, from: :file, file: identity[:privkey_file] } + when :privkey_file + private_key = Net::SSH::KeyFactory.load_private_key( + identity[:privkey_file], options[:passphrase], ask_passphrase, options[:password_prompt] + ) + key = private_key.send(:public_key) + if @deprecated_rsa_sha1 && key.respond_to?(:deprecated_rsa_sha1=) + key.deprecated_rsa_sha1 = true + private_key.deprecated_rsa_sha1 = true + Vagrant.global_logger.debug("set RSA SHA1 deprecation on public key: #{key.fingerprint}") + Vagrant.global_logger.debug("set RSA SHA1 deprecation on private key: #{private_key.fingerprint}") + end + { public_key: key, from: :file, file: identity[:privkey_file], key: private_key } + when :data + private_key = Net::SSH::KeyFactory.load_data_private_key( + identity[:data], options[:passphrase], ask_passphrase, "", options[:password_prompt] + ) + key = private_key.send(:public_key) + if @deprecated_rsa_sha1 && key.respond_to?(:deprecated_rsa_sha1=) + key.deprecated_rsa_sha1 = true + private_key.deprecated_rsa_sha1 = true + Vagrant.global_logger.debug("set RSA SHA1 deprecation on public key: #{key.fingerprint}") + Vagrant.global_logger.debug("set RSA SHA1 deprecation on private key: #{private_key.fingerprint}") + end + { public_key: key, from: :key_data, data: identity[:data], key: private_key } + else + identity + end + rescue OpenSSL::PKey::RSAError, OpenSSL::PKey::DSAError, OpenSSL::PKey::ECError, OpenSSL::PKey::PKeyError, ArgumentError => e + if ignore_decryption_errors + identity + else + process_identity_loading_error(identity, e) + nil + end + rescue Exception => e + process_identity_loading_error(identity, e) + nil + end + end.compact + end + end + + module AuthenticationSession + def initialize(transport, options={}) + s_ver_str = transport.server_version.version. + match(/OpenSSH_.*?(?\d+\.\d+)/)&.[](:version).to_s + Vagrant.global_logger.debug("ssh server version detected: #{s_ver_str}") + if !s_ver_str.empty? + begin + ver = Gem::Version.new(s_ver_str) + if ver >= Gem::Version.new("7.2") + Vagrant.global_logger.debug("ssh server supports deprecation of RSA SHA1, deprecating") + options[:deprecated_rsa_sha1] = true + else + Vagrant.global_logger.debug("ssh server does not support deprecation of RSA SHA1") + end + rescue ArgumentError => err + Vagrant.global_logger.debug("failed to determine valid ssh server version - #{err}") + end + end + super end - _raw_build_request(pub_key, *args) end - alias_method :_raw_build_request, :build_request - alias_method :build_request, :rsa_compat_build_request end + require "net/ssh/authentication/key_manager" + Net::SSH::Authentication::KeyManager.prepend(DeprecatedRsaSha1::KeyManager) + require "net/ssh/authentication/session" + Net::SSH::Authentication::Session.prepend(DeprecatedRsaSha1::AuthenticationSession) + require "net/ssh/authentication/agent" # net/ssh/authentication/agent Net::SSH::Authentication::Agent.class_eval do @@ -67,7 +157,10 @@ if Net::SSH::Version::STRING == "6.1.0" require "net/ssh/transport/algorithms" # net/ssh/transport/algorithms - Net::SSH::Transport::Algorithms::DEFAULT_ALGORITHMS[:host_key].push("rsa-sha2-256").push("rsa-sha2-512") + Net::SSH::Transport::Algorithms::DEFAULT_ALGORITHMS[:host_key].insert( + Net::SSH::Transport::Algorithms::DEFAULT_ALGORITHMS[:host_key].size - 1, "rsa-sha2-256") + Net::SSH::Transport::Algorithms::DEFAULT_ALGORITHMS[:host_key].insert( + Net::SSH::Transport::Algorithms::DEFAULT_ALGORITHMS[:host_key].size - 1, "rsa-sha2-512") require "net/ssh/transport/cipher_factory" # net/ssh/transport/cipher_factory @@ -113,7 +206,7 @@ if Net::SSH::Version::STRING == "6.1.0" require "net/ssh/transport/openssl" # net/ssh/transport/openssl OpenSSL::PKey::RSA.class_eval do - attr_accessor :deprecated_ssh_rsa + attr_accessor :deprecated_rsa_sha1 def ssh_do_verify(sig, data, options = {}) digester = @@ -129,7 +222,7 @@ if Net::SSH::Version::STRING == "6.1.0" end def ssh_type - deprecated_ssh_rsa ? signature_algorithm : "ssh-rsa" + deprecated_rsa_sha1 ? signature_algorithm : "ssh-rsa" end def signature_algorithm @@ -137,7 +230,7 @@ if Net::SSH::Version::STRING == "6.1.0" end def ssh_do_sign(data) - if deprecated_ssh_rsa + if deprecated_rsa_sha1 sign(OpenSSL::Digest::SHA256.new, data) else sign(OpenSSL::Digest::SHA1.new, data)