summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorZdenek Zambersky <zzambers@redhat.com>2021-05-06 13:50:20 +0200
committerFlorian Wininger <fw.centrale@gmail.com>2021-11-18 13:27:41 +0100
commita45f54fe1de434605af0b7195dd9a91bccd2cec5 (patch)
tree2a2b52c5bf7ac31804d1d6f018a4b5ddf805ca79 /lib
parent0150d054f0cd0beacd4ba1000c6df6d8636a2c18 (diff)
downloadnet-ssh-a45f54fe1de434605af0b7195dd9a91bccd2cec5.tar.gz
Added support for RSA client authentication with SHA-2
Diffstat (limited to 'lib')
-rw-r--r--lib/net/ssh/authentication/certificate.rb4
-rw-r--r--lib/net/ssh/authentication/ed25519.rb2
-rw-r--r--lib/net/ssh/authentication/key_manager.rb21
-rw-r--r--lib/net/ssh/authentication/methods/abstract.rb10
-rw-r--r--lib/net/ssh/authentication/methods/publickey.rb66
-rw-r--r--lib/net/ssh/authentication/session.rb4
-rw-r--r--lib/net/ssh/transport/openssl.rb16
7 files changed, 99 insertions, 24 deletions
diff --git a/lib/net/ssh/authentication/certificate.rb b/lib/net/ssh/authentication/certificate.rb
index c9619fa..5250789 100644
--- a/lib/net/ssh/authentication/certificate.rb
+++ b/lib/net/ssh/authentication/certificate.rb
@@ -66,8 +66,8 @@ module Net
).to_s
end
- def ssh_do_sign(data)
- key.ssh_do_sign(data)
+ def ssh_do_sign(data, sig_alg = nil)
+ key.ssh_do_sign(data, sig_alg)
end
def ssh_do_verify(sig, data, options = {})
diff --git a/lib/net/ssh/authentication/ed25519.rb b/lib/net/ssh/authentication/ed25519.rb
index c2a117a..dccc64f 100644
--- a/lib/net/ssh/authentication/ed25519.rb
+++ b/lib/net/ssh/authentication/ed25519.rb
@@ -171,7 +171,7 @@ module Net
PubKey.new(@pk)
end
- def ssh_do_sign(data)
+ def ssh_do_sign(data, sig_alg = nil)
@sign_key.sign(data)
end
diff --git a/lib/net/ssh/authentication/key_manager.rb b/lib/net/ssh/authentication/key_manager.rb
index ac86cc8..563702c 100644
--- a/lib/net/ssh/authentication/key_manager.rb
+++ b/lib/net/ssh/authentication/key_manager.rb
@@ -158,7 +158,7 @@ module Net
# Regardless of the identity's origin or who does the signing, this
# will always return the signature in an SSH2-specified "signature
# blob" format.
- def sign(identity, data)
+ def sign(identity, data, sig_alg = nil)
info = known_identities[identity] or raise KeyManagerError, "the given identity is unknown to the key manager"
if info[:key].nil? && info[:from] == :file
@@ -170,14 +170,27 @@ module Net
end
if info[:key]
- return Net::SSH::Buffer.from(:string, identity.ssh_signature_type,
- :mstring, info[:key].ssh_do_sign(data.to_s)).to_s
+ if sig_alg.nil?
+ signed = info[:key].ssh_do_sign(data.to_s)
+ sig_alg = identity.ssh_signature_type
+ else
+ signed = info[:key].ssh_do_sign(data.to_s, sig_alg)
+ end
+ return Net::SSH::Buffer.from(:string, sig_alg,
+ :mstring, signed).to_s
end
if info[:from] == :agent
raise KeyManagerError, "the agent is no longer available" unless agent
- return agent.sign(info[:identity], data.to_s)
+ case sig_alg
+ when "rsa-sha2-512"
+ return agent.sign(info[:identity], data.to_s, Net::SSH::Authentication::Agent::SSH_AGENT_RSA_SHA2_512)
+ when "rsa-sha2-256"
+ return agent.sign(info[:identity], data.to_s, Net::SSH::Authentication::Agent::SSH_AGENT_RSA_SHA2_256)
+ else
+ return agent.sign(info[:identity], data.to_s)
+ end
end
raise KeyManagerError, "[BUG] can't determine identity origin (#{info.inspect})"
diff --git a/lib/net/ssh/authentication/methods/abstract.rb b/lib/net/ssh/authentication/methods/abstract.rb
index 07aa5f3..f023011 100644
--- a/lib/net/ssh/authentication/methods/abstract.rb
+++ b/lib/net/ssh/authentication/methods/abstract.rb
@@ -20,12 +20,22 @@ module Net
# this.
attr_reader :key_manager
+ # So far only affects algorithms used for rsa keys, but can be
+ # extended to other keys, e.g after reading of
+ # PubkeyAcceptedAlgorithms option from ssh_config file is implemented.
+ attr_reader :pubkey_algorithms
+
# Instantiates a new authentication method.
def initialize(session, options = {})
@session = session
@key_manager = options[:key_manager]
@options = options
@prompt = options[:password_prompt]
+ @pubkey_algorithms = options[:pubkey_algorithms] \
+ || %w[rsa-sha2-256-cert-v01@openssh.com
+ ssh-rsa-cert-v01@openssh.com
+ rsa-sha2-256
+ ssh-rsa]
self.logger = session.logger
end
diff --git a/lib/net/ssh/authentication/methods/publickey.rb b/lib/net/ssh/authentication/methods/publickey.rb
index eb4d740..48a56ab 100644
--- a/lib/net/ssh/authentication/methods/publickey.rb
+++ b/lib/net/ssh/authentication/methods/publickey.rb
@@ -26,41 +26,40 @@ module Net
# Builds a packet that contains the request formatted for sending
# a public-key request to the server.
- def build_request(pub_key, username, next_service, has_sig)
+ def build_request(pub_key, username, next_service, alg, has_sig)
blob = Net::SSH::Buffer.new
blob.write_key pub_key
userauth_request(username, next_service, "publickey", has_sig,
- pub_key.ssh_type, blob.to_s)
+ alg, blob.to_s)
end
# Builds and sends a request formatted for a public-key
# authentication request.
- def send_request(pub_key, username, next_service, signature = nil)
- msg = build_request(pub_key, username, next_service, !signature.nil?)
+ def send_request(pub_key, username, next_service, alg, signature = nil)
+ msg = build_request(pub_key, username, next_service, alg,
+ !signature.nil?)
msg.write_string(signature) if signature
send_message(msg)
end
- # Attempts to perform public-key authentication for the given
- # username, with the given identity (public key). Returns +true+ if
- # successful, or +false+ otherwise.
- def authenticate_with(identity, next_service, username)
+ def authenticate_with_alg(identity, next_service, username, alg, sig_alg = nil)
debug { "trying publickey (#{identity.fingerprint})" }
- send_request(identity, username, next_service)
+ send_request(identity, username, next_service, alg)
message = session.next_message
case message.type
when USERAUTH_PK_OK
- buffer = build_request(identity, username, next_service, true)
+ buffer = build_request(identity, username, next_service, alg,
+ true)
sig_data = Net::SSH::Buffer.new
sig_data.write_string(session_id)
sig_data.append(buffer.to_s)
- sig_blob = key_manager.sign(identity, sig_data)
+ sig_blob = key_manager.sign(identity, sig_data, sig_alg)
- send_request(identity, username, next_service, sig_blob.to_s)
+ send_request(identity, username, next_service, alg, sig_blob.to_s)
message = session.next_message
case message.type
@@ -88,6 +87,49 @@ module Net
raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
end
end
+
+ # Attempts to perform public-key authentication for the given
+ # username, with the given identity (public key). Returns +true+ if
+ # successful, or +false+ otherwise.
+ def authenticate_with(identity, next_service, username)
+ type = identity.ssh_type
+ if type == "ssh-rsa"
+ pubkey_algorithms.each do |pk_alg|
+ case pk_alg
+ when "rsa-sha2-512", "rsa-sha2-256", "ssh-rsa"
+ if authenticate_with_alg(identity, next_service, username, pk_alg, pk_alg)
+ # success
+ return true
+ end
+ end
+ end
+ elsif type == "ssh-rsa-cert-v01@openssh.com"
+ pubkey_algorithms.each do |pk_alg|
+ case pk_alg
+ when "rsa-sha2-512-cert-v01@openssh.com"
+ if authenticate_with_alg(identity, next_service, username, pk_alg, "rsa-sha2-512")
+ # success
+ return true
+ end
+ when "rsa-sha2-256-cert-v01@openssh.com"
+ if authenticate_with_alg(identity, next_service, username, pk_alg, "rsa-sha2-256")
+ # success
+ return true
+ end
+ when "ssh-rsa-cert-v01@openssh.com"
+ if authenticate_with_alg(identity, next_service, username, pk_alg)
+ # success
+ return true
+ end
+ end
+ end
+ elsif authenticate_with_alg(identity, next_service, username, type)
+ # success
+ return true
+ end
+ # failure
+ return false
+ end
end
end
end
diff --git a/lib/net/ssh/authentication/session.rb b/lib/net/ssh/authentication/session.rb
index e4fc648..773d704 100644
--- a/lib/net/ssh/authentication/session.rb
+++ b/lib/net/ssh/authentication/session.rb
@@ -76,7 +76,9 @@ module Net
debug { "trying #{name}" }
begin
auth_class = Methods.const_get(name.split(/\W+/).map { |p| p.capitalize }.join)
- method = auth_class.new(self, key_manager: key_manager, password_prompt: options[:password_prompt])
+ method = auth_class.new(self,
+ key_manager: key_manager, password_prompt: options[:password_prompt],
+ pubkey_algorithms: options[:pubkey_algorithms] || nil)
rescue NameError
debug {"Mechanism #{name} was requested, but isn't a known type. Ignoring it."}
next
diff --git a/lib/net/ssh/transport/openssl.rb b/lib/net/ssh/transport/openssl.rb
index 4ff58cf..1c23651 100644
--- a/lib/net/ssh/transport/openssl.rb
+++ b/lib/net/ssh/transport/openssl.rb
@@ -74,8 +74,16 @@ module OpenSSL
end
# Returns the signature for the given data.
- def ssh_do_sign(data)
- sign(OpenSSL::Digest::SHA1.new, data)
+ def ssh_do_sign(data, sig_alg = nil)
+ digester =
+ if sig_alg == "rsa-sha2-512"
+ OpenSSL::Digest::SHA512.new
+ elsif sig_alg == "rsa-sha2-256"
+ OpenSSL::Digest::SHA256.new
+ else
+ OpenSSL::Digest::SHA1.new
+ end
+ sign(digester, data)
end
end
@@ -109,7 +117,7 @@ module OpenSSL
end
# Signs the given data.
- def ssh_do_sign(data)
+ def ssh_do_sign(data, sig_alg = nil)
sig = sign(OpenSSL::Digest::SHA1.new, data)
a1sig = OpenSSL::ASN1.decode(sig)
@@ -220,7 +228,7 @@ module OpenSSL
end
# Returns the signature for the given data.
- def ssh_do_sign(data)
+ def ssh_do_sign(data, sig_alg = nil)
digest = digester.digest(data)
sig = dsa_sign_asn1(digest)
a1sig = OpenSSL::ASN1.decode(sig)