Flag RSA SHA1 deprecation when loading keys

Set flag on RSA keys of deprecated RSA SHA1 support when loading
    keys based on server version of the transport. This ensures keys
    are properly flagged. Flag name has been updated to provide context
    on usage.

    Version matching on the OpenSSH server version has also been updated
    to handle customized naming in the version string (as seen in the
    Windows port) and to properly handle when no match is found.

    Fixes #12344 #12408 #12381
This commit is contained in:
Chris Roberts 2021-06-10 20:59:43 -07:00
parent dd99138a90
commit 46aca8be5a

View File

@ -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_(?<version>\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, "<key in memory>", 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_.*?(?<version>\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)