net-ssh 6.3.0.beta1 → 7.0.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.dockerignore +6 -0
  4. data/.github/config/rubocop_linter_action.yml +4 -0
  5. data/.github/workflows/ci-with-docker.yml +44 -0
  6. data/.github/workflows/ci.yml +4 -10
  7. data/.github/workflows/rubocop.yml +13 -0
  8. data/.rubocop.yml +2 -1
  9. data/.rubocop_todo.yml +244 -237
  10. data/Dockerfile +27 -0
  11. data/Dockerfile.openssl3 +17 -0
  12. data/README.md +7 -1
  13. data/Rakefile +4 -0
  14. data/docker-compose.yml +23 -0
  15. data/lib/net/ssh/authentication/agent.rb +13 -13
  16. data/lib/net/ssh/authentication/certificate.rb +4 -4
  17. data/lib/net/ssh/authentication/ed25519.rb +5 -5
  18. data/lib/net/ssh/authentication/key_manager.rb +18 -5
  19. data/lib/net/ssh/authentication/methods/abstract.rb +12 -2
  20. data/lib/net/ssh/authentication/methods/hostbased.rb +3 -3
  21. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +1 -1
  22. data/lib/net/ssh/authentication/methods/none.rb +1 -1
  23. data/lib/net/ssh/authentication/methods/password.rb +1 -1
  24. data/lib/net/ssh/authentication/methods/publickey.rb +56 -14
  25. data/lib/net/ssh/authentication/pageant.rb +8 -8
  26. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +2 -2
  27. data/lib/net/ssh/authentication/session.rb +5 -3
  28. data/lib/net/ssh/buffer.rb +41 -26
  29. data/lib/net/ssh/buffered_io.rb +6 -6
  30. data/lib/net/ssh/config.rb +4 -4
  31. data/lib/net/ssh/connection/channel.rb +13 -13
  32. data/lib/net/ssh/connection/event_loop.rb +8 -8
  33. data/lib/net/ssh/connection/session.rb +13 -13
  34. data/lib/net/ssh/errors.rb +2 -2
  35. data/lib/net/ssh/key_factory.rb +7 -7
  36. data/lib/net/ssh/known_hosts.rb +5 -4
  37. data/lib/net/ssh/prompt.rb +1 -1
  38. data/lib/net/ssh/proxy/http.rb +1 -1
  39. data/lib/net/ssh/proxy/https.rb +2 -2
  40. data/lib/net/ssh/proxy/socks4.rb +1 -1
  41. data/lib/net/ssh/proxy/socks5.rb +1 -1
  42. data/lib/net/ssh/service/forward.rb +4 -4
  43. data/lib/net/ssh/test/channel.rb +3 -3
  44. data/lib/net/ssh/test/extensions.rb +6 -6
  45. data/lib/net/ssh/test/packet.rb +1 -1
  46. data/lib/net/ssh/test/script.rb +3 -3
  47. data/lib/net/ssh/test/socket.rb +1 -1
  48. data/lib/net/ssh/test.rb +3 -3
  49. data/lib/net/ssh/transport/algorithms.rb +12 -12
  50. data/lib/net/ssh/transport/cipher_factory.rb +15 -15
  51. data/lib/net/ssh/transport/ctr.rb +3 -3
  52. data/lib/net/ssh/transport/hmac/abstract.rb +4 -4
  53. data/lib/net/ssh/transport/hmac.rb +12 -12
  54. data/lib/net/ssh/transport/identity_cipher.rb +1 -1
  55. data/lib/net/ssh/transport/kex/abstract.rb +3 -3
  56. data/lib/net/ssh/transport/kex/abstract5656.rb +1 -1
  57. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +1 -1
  58. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +21 -21
  59. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +1 -1
  60. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +2 -2
  61. data/lib/net/ssh/transport/kex.rb +7 -7
  62. data/lib/net/ssh/transport/key_expander.rb +1 -1
  63. data/lib/net/ssh/transport/openssl.rb +32 -11
  64. data/lib/net/ssh/transport/packet_stream.rb +1 -1
  65. data/lib/net/ssh/transport/session.rb +6 -6
  66. data/lib/net/ssh/transport/state.rb +1 -1
  67. data/lib/net/ssh/version.rb +2 -2
  68. data/lib/net/ssh.rb +3 -3
  69. data/net-ssh.gemspec +2 -2
  70. data.tar.gz.sig +0 -0
  71. metadata +13 -7
  72. metadata.gz.sig +1 -2
  73. data/.travis.yml +0 -51
data/Dockerfile ADDED
@@ -0,0 +1,27 @@
1
+ ARG RUBY_VERSION=3.1
2
+ FROM ruby:${RUBY_VERSION}
3
+
4
+ RUN apt update && apt install -y openssh-server sudo netcat \
5
+ && useradd --create-home --shell '/bin/bash' --comment 'NetSSH' 'net_ssh_1' \
6
+ && useradd --create-home --shell '/bin/bash' --comment 'NetSSH' 'net_ssh_2' \
7
+ && echo net_ssh_1:foopwd | chpasswd \
8
+ && echo net_ssh_2:foo2pwd | chpasswd \
9
+ && mkdir -p /home/net_ssh_1/.ssh \
10
+ && mkdir -p /home/net_ssh_2/.ssh \
11
+ && echo "net_ssh_1 ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers \
12
+ && echo "net_ssh_2 ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers \
13
+ && ssh-keygen -f /etc/ssh/users_ca -N ''
14
+
15
+ ENV INSTALL_PATH="/netssh"
16
+
17
+ WORKDIR $INSTALL_PATH
18
+
19
+ COPY Gemfile net-ssh.gemspec $INSTALL_PATH/
20
+
21
+ COPY lib/net/ssh/version.rb $INSTALL_PATH/lib/net/ssh/version.rb
22
+
23
+ RUN gem install bundler && bundle install
24
+
25
+ COPY . $INSTALL_PATH/
26
+
27
+ CMD service ssh start && rake test && NET_SSH_NO_ED25519=1 rake test
@@ -0,0 +1,17 @@
1
+ FROM ubuntu:22.04
2
+
3
+ ENV INSTALL_PATH="/netssh"
4
+
5
+ RUN apt update && apt install -y openssl ruby ruby-dev git build-essential
6
+
7
+ WORKDIR $INSTALL_PATH
8
+
9
+ COPY Gemfile net-ssh.gemspec $INSTALL_PATH/
10
+
11
+ COPY lib/net/ssh/version.rb $INSTALL_PATH/lib/net/ssh/version.rb
12
+
13
+ RUN ls -l && gem install bundler && bundle install
14
+
15
+ COPY . $INSTALL_PATH/
16
+
17
+ CMD openssl version && ruby -ropenssl -e 'puts OpenSSL::OPENSSL_VERSION' && rake test
data/README.md CHANGED
@@ -211,13 +211,19 @@ Run the test suite from the net-ssh directory with the following command:
211
211
  bundle exec rake test
212
212
  ```
213
213
 
214
+ NOTE : you can run test on all ruby versions with docker :
215
+
216
+ ```
217
+ docker-compose up --build
218
+ ```
219
+
214
220
  Run a single test file like this:
215
221
 
216
222
  ```sh
217
223
  ruby -Ilib -Itest test/transport/test_server_version.rb
218
224
  ```
219
225
 
220
- To run integration tests see test/integration/README.txt
226
+ To run integration tests see [here](test/integration/README.md)
221
227
 
222
228
  ### BUILDING GEM
223
229
 
data/Rakefile CHANGED
@@ -95,6 +95,10 @@ Rake::TestTask.new do |t|
95
95
  t.test_files = test_files
96
96
  end
97
97
 
98
+ # We need to enable the OpenSSL 3.0 legacy providers for our test suite
99
+ require 'openssl'
100
+ ENV['OPENSSL_CONF'] = 'test/openssl3.conf' if OpenSSL::OPENSSL_LIBRARY_VERSION.start_with? "OpenSSL 3"
101
+
98
102
  desc "Run tests of Net::SSH:Test"
99
103
  Rake::TestTask.new do |t|
100
104
  t.name = "test_test"
@@ -0,0 +1,23 @@
1
+ version: '3'
2
+
3
+ services:
4
+ ruby-3.1:
5
+ build:
6
+ context: .
7
+ args:
8
+ RUBY_VERSION: 3.1
9
+ ruby-3.0:
10
+ build:
11
+ context: .
12
+ args:
13
+ RUBY_VERSION: 3.0
14
+ ruby-2.7:
15
+ build:
16
+ context: .
17
+ args:
18
+ RUBY_VERSION: 2.7
19
+ ruby-2.6:
20
+ build:
21
+ context: .
22
+ args:
23
+ RUBY_VERSION: 2.6
@@ -65,7 +65,7 @@ module Net
65
65
 
66
66
  # Instantiates a new agent object, connects to a running SSH agent,
67
67
  # negotiates the agent protocol version, and returns the agent object.
68
- def self.connect(logger=nil, agent_socket_factory = nil, identity_agent = nil)
68
+ def self.connect(logger = nil, agent_socket_factory = nil, identity_agent = nil)
69
69
  agent = new(logger)
70
70
  agent.connect!(agent_socket_factory, identity_agent)
71
71
  agent.negotiate!
@@ -74,7 +74,7 @@ module Net
74
74
 
75
75
  # Creates a new Agent object, using the optional logger instance to
76
76
  # report status.
77
- def initialize(logger=nil)
77
+ def initialize(logger = nil)
78
78
  self.logger = logger
79
79
  end
80
80
 
@@ -88,9 +88,9 @@ module Net
88
88
  if agent_socket_factory
89
89
  agent_socket_factory.call
90
90
  elsif identity_agent
91
- unix_socket_class.open(identity_agent)
91
+ unix_socket_class.open(File.expand_path(identity_agent))
92
92
  elsif ENV['SSH_AUTH_SOCK'] && unix_socket_class
93
- unix_socket_class.open(ENV['SSH_AUTH_SOCK'])
93
+ unix_socket_class.open(File.expand_path(ENV['SSH_AUTH_SOCK']))
94
94
  elsif Gem.win_platform? && RUBY_ENGINE != "jruby"
95
95
  Pageant::Socket.open
96
96
  else
@@ -177,7 +177,7 @@ module Net
177
177
 
178
178
  req_type = constraints.empty? ? SSH2_AGENT_ADD_IDENTITY : SSH2_AGENT_ADD_ID_CONSTRAINED
179
179
  type, = send_and_wait(req_type, :string, priv_key.ssh_type, :raw, blob_for_add(priv_key),
180
- :string, comment, :raw, constraints)
180
+ :string, comment, :raw, constraints)
181
181
  raise AgentError, "could not add identity to agent" if type != SSH_AGENT_SUCCESS
182
182
  end
183
183
 
@@ -251,31 +251,31 @@ module Net
251
251
  case priv_key.ssh_type
252
252
  when /^ssh-dss$/
253
253
  Net::SSH::Buffer.from(:bignum, priv_key.p, :bignum, priv_key.q, :bignum, priv_key.g,
254
- :bignum, priv_key.pub_key, :bignum, priv_key.priv_key).to_s
254
+ :bignum, priv_key.pub_key, :bignum, priv_key.priv_key).to_s
255
255
  when /^ssh-dss-cert-v01@openssh\.com$/
256
256
  Net::SSH::Buffer.from(:string, priv_key.to_blob, :bignum, priv_key.key.priv_key).to_s
257
257
  when /^ecdsa\-sha2\-(\w*)$/
258
258
  curve_name = OpenSSL::PKey::EC::CurveNameAliasInv[priv_key.group.curve_name]
259
259
  Net::SSH::Buffer.from(:string, curve_name, :mstring, priv_key.public_key.to_bn.to_s(2),
260
- :bignum, priv_key.private_key).to_s
260
+ :bignum, priv_key.private_key).to_s
261
261
  when /^ecdsa\-sha2\-(\w*)-cert-v01@openssh\.com$/
262
262
  Net::SSH::Buffer.from(:string, priv_key.to_blob, :bignum, priv_key.key.private_key).to_s
263
263
  when /^ssh-ed25519$/
264
264
  Net::SSH::Buffer.from(:string, priv_key.public_key.verify_key.to_bytes,
265
- :string, priv_key.sign_key.keypair).to_s
265
+ :string, priv_key.sign_key.keypair).to_s
266
266
  when /^ssh-ed25519-cert-v01@openssh\.com$/
267
267
  # Unlike the other certificate types, the public key is included after the certifiate.
268
268
  Net::SSH::Buffer.from(:string, priv_key.to_blob,
269
- :string, priv_key.key.public_key.verify_key.to_bytes,
270
- :string, priv_key.key.sign_key.keypair).to_s
269
+ :string, priv_key.key.public_key.verify_key.to_bytes,
270
+ :string, priv_key.key.sign_key.keypair).to_s
271
271
  when /^ssh-rsa$/
272
272
  # `n` and `e` are reversed compared to the ordering in `OpenSSL::PKey::RSA#to_blob`.
273
273
  Net::SSH::Buffer.from(:bignum, priv_key.n, :bignum, priv_key.e, :bignum, priv_key.d,
274
- :bignum, priv_key.iqmp, :bignum, priv_key.p, :bignum, priv_key.q).to_s
274
+ :bignum, priv_key.iqmp, :bignum, priv_key.p, :bignum, priv_key.q).to_s
275
275
  when /^ssh-rsa-cert-v01@openssh\.com$/
276
276
  Net::SSH::Buffer.from(:string, priv_key.to_blob, :bignum, priv_key.key.d,
277
- :bignum, priv_key.key.iqmp, :bignum, priv_key.key.p,
278
- :bignum, priv_key.key.q).to_s
277
+ :bignum, priv_key.key.iqmp, :bignum, priv_key.key.p,
278
+ :bignum, priv_key.key.q).to_s
279
279
  end
280
280
  end
281
281
  end
@@ -66,8 +66,8 @@ module Net
66
66
  ).to_s
67
67
  end
68
68
 
69
- def ssh_do_sign(data)
70
- key.ssh_do_sign(data)
69
+ def ssh_do_sign(data, sig_alg = nil)
70
+ key.ssh_do_sign(data, sig_alg)
71
71
  end
72
72
 
73
73
  def ssh_do_verify(sig, data, options = {})
@@ -83,7 +83,7 @@ module Net
83
83
  end
84
84
 
85
85
  # Signs the certificate with key.
86
- def sign!(key, sign_nonce=nil)
86
+ def sign!(key, sign_nonce = nil)
87
87
  # ssh-keygen uses 32 bytes of nonce.
88
88
  self.nonce = sign_nonce || SecureRandom.random_bytes(32)
89
89
  self.signature_key = key
@@ -94,7 +94,7 @@ module Net
94
94
  self
95
95
  end
96
96
 
97
- def sign(key, sign_nonce=nil)
97
+ def sign(key, sign_nonce = nil)
98
98
  cert = clone
99
99
  cert.sign!(key, sign_nonce)
100
100
  end
@@ -14,7 +14,7 @@ module Net
14
14
  module Authentication
15
15
  module ED25519
16
16
  class SigningKeyFromFile < SimpleDelegator
17
- def initialize(pk,sk)
17
+ def initialize(pk, sk)
18
18
  key = ::Ed25519::SigningKey.from_keypair(sk)
19
19
  raise ArgumentError, "pk does not match sk" unless pk == key.verify_key.to_bytes
20
20
 
@@ -116,7 +116,7 @@ module Net
116
116
  end
117
117
 
118
118
  def to_blob
119
- Net::SSH::Buffer.from(:mstring,"ssh-ed25519".dup,:string,@verify_key.to_bytes).to_s
119
+ Net::SSH::Buffer.from(:mstring, "ssh-ed25519".dup, :string, @verify_key.to_bytes).to_s
120
120
  end
121
121
 
122
122
  def ssh_type
@@ -128,7 +128,7 @@ module Net
128
128
  end
129
129
 
130
130
  def ssh_do_verify(sig, data, options = {})
131
- @verify_key.verify(sig,data)
131
+ @verify_key.verify(sig, data)
132
132
  end
133
133
 
134
134
  def to_pem
@@ -152,7 +152,7 @@ module Net
152
152
  _comment = buffer.read_string
153
153
 
154
154
  @pk = pk
155
- @sign_key = SigningKeyFromFile.new(pk,sk)
155
+ @sign_key = SigningKeyFromFile.new(pk, sk)
156
156
  end
157
157
 
158
158
  def to_blob
@@ -171,7 +171,7 @@ module Net
171
171
  PubKey.new(@pk)
172
172
  end
173
173
 
174
- def ssh_do_sign(data)
174
+ def ssh_do_sign(data, sig_alg = nil)
175
175
  @sign_key.sign(data)
176
176
  end
177
177
 
@@ -41,7 +41,7 @@ module Net
41
41
  # Create a new KeyManager. By default, the manager will
42
42
  # use the ssh-agent if it is running and the `:use_agent` option
43
43
  # is not false.
44
- def initialize(logger, options={})
44
+ def initialize(logger, options = {})
45
45
  self.logger = logger
46
46
  @key_files = []
47
47
  @key_data = []
@@ -158,7 +158,7 @@ module Net
158
158
  # Regardless of the identity's origin or who does the signing, this
159
159
  # will always return the signature in an SSH2-specified "signature
160
160
  # blob" format.
161
- def sign(identity, data)
161
+ def sign(identity, data, sig_alg = nil)
162
162
  info = known_identities[identity] or raise KeyManagerError, "the given identity is unknown to the key manager"
163
163
 
164
164
  if info[:key].nil? && info[:from] == :file
@@ -170,14 +170,27 @@ module Net
170
170
  end
171
171
 
172
172
  if info[:key]
173
- return Net::SSH::Buffer.from(:string, identity.ssh_signature_type,
174
- :mstring, info[:key].ssh_do_sign(data.to_s)).to_s
173
+ if sig_alg.nil?
174
+ signed = info[:key].ssh_do_sign(data.to_s)
175
+ sig_alg = identity.ssh_signature_type
176
+ else
177
+ signed = info[:key].ssh_do_sign(data.to_s, sig_alg)
178
+ end
179
+ return Net::SSH::Buffer.from(:string, sig_alg,
180
+ :mstring, signed).to_s
175
181
  end
176
182
 
177
183
  if info[:from] == :agent
178
184
  raise KeyManagerError, "the agent is no longer available" unless agent
179
185
 
180
- return agent.sign(info[:identity], data.to_s)
186
+ case sig_alg
187
+ when "rsa-sha2-512"
188
+ return agent.sign(info[:identity], data.to_s, Net::SSH::Authentication::Agent::SSH_AGENT_RSA_SHA2_512)
189
+ when "rsa-sha2-256"
190
+ return agent.sign(info[:identity], data.to_s, Net::SSH::Authentication::Agent::SSH_AGENT_RSA_SHA2_256)
191
+ else
192
+ return agent.sign(info[:identity], data.to_s)
193
+ end
181
194
  end
182
195
 
183
196
  raise KeyManagerError, "[BUG] can't determine identity origin (#{info.inspect})"
@@ -20,12 +20,22 @@ module Net
20
20
  # this.
21
21
  attr_reader :key_manager
22
22
 
23
+ # So far only affects algorithms used for rsa keys, but can be
24
+ # extended to other keys, e.g after reading of
25
+ # PubkeyAcceptedAlgorithms option from ssh_config file is implemented.
26
+ attr_reader :pubkey_algorithms
27
+
23
28
  # Instantiates a new authentication method.
24
- def initialize(session, options={})
29
+ def initialize(session, options = {})
25
30
  @session = session
26
31
  @key_manager = options[:key_manager]
27
32
  @options = options
28
33
  @prompt = options[:password_prompt]
34
+ @pubkey_algorithms = options[:pubkey_algorithms] \
35
+ || %w[rsa-sha2-256-cert-v01@openssh.com
36
+ ssh-rsa-cert-v01@openssh.com
37
+ rsa-sha2-256
38
+ ssh-rsa]
29
39
  self.logger = session.logger
30
40
  end
31
41
 
@@ -46,7 +56,7 @@ module Net
46
56
  # of the packet. The new packet is returned, ready for sending.
47
57
  def userauth_request(username, next_service, auth_method, *others)
48
58
  buffer = Net::SSH::Buffer.from(:byte, USERAUTH_REQUEST,
49
- :string, username, :string, next_service, :string, auth_method)
59
+ :string, username, :string, next_service, :string, auth_method)
50
60
 
51
61
  others.each do |value|
52
62
  case value
@@ -10,12 +10,12 @@ module Net
10
10
 
11
11
  # Attempts to perform host-based authorization of the user by trying
12
12
  # all known keys.
13
- def authenticate(next_service, username, password=nil)
13
+ def authenticate(next_service, username, password = nil)
14
14
  return false unless key_manager
15
15
 
16
16
  key_manager.each_identity do |identity|
17
17
  return true if authenticate_with(identity, next_service,
18
- username, key_manager)
18
+ username, key_manager)
19
19
  end
20
20
 
21
21
  return false
@@ -63,7 +63,7 @@ module Net
63
63
  # Build the "core" hostbased request string.
64
64
  def build_request(identity, next_service, username, hostname, client_username)
65
65
  userauth_request(username, next_service, "hostbased", identity.ssh_type,
66
- Buffer.from(:key, identity).to_s, hostname, client_username).to_s
66
+ Buffer.from(:key, identity).to_s, hostname, client_username).to_s
67
67
  end
68
68
  end
69
69
  end
@@ -11,7 +11,7 @@ module Net
11
11
  USERAUTH_INFO_RESPONSE = 61
12
12
 
13
13
  # Attempt to authenticate the given user for the given service.
14
- def authenticate(next_service, username, password=nil)
14
+ def authenticate(next_service, username, password = nil)
15
15
  debug { "trying keyboard-interactive" }
16
16
  send_message(userauth_request(username, next_service, "keyboard-interactive", "", ""))
17
17
 
@@ -8,7 +8,7 @@ module Net
8
8
  # Implements the "none" SSH authentication method.
9
9
  class None < Abstract
10
10
  # Attempt to authenticate as "none"
11
- def authenticate(next_service, user="", password="")
11
+ def authenticate(next_service, user = "", password = "")
12
12
  send_message(userauth_request(user, next_service, "none"))
13
13
  message = session.next_message
14
14
 
@@ -10,7 +10,7 @@ module Net
10
10
  class Password < Abstract
11
11
  # Attempt to authenticate the given user for the given service. If
12
12
  # the password parameter is nil, this will ask for password
13
- def authenticate(next_service, username, password=nil)
13
+ def authenticate(next_service, username, password = nil)
14
14
  clear_prompter!
15
15
  retries = 0
16
16
  max_retries = get_max_retries
@@ -12,7 +12,7 @@ module Net
12
12
  # username, trying each identity known to the key manager. If any of
13
13
  # them succeed, returns +true+, otherwise returns +false+. This
14
14
  # requires the presence of a key manager.
15
- def authenticate(next_service, username, password=nil)
15
+ def authenticate(next_service, username, password = nil)
16
16
  return false unless key_manager
17
17
 
18
18
  key_manager.each_identity do |identity|
@@ -26,41 +26,40 @@ module Net
26
26
 
27
27
  # Builds a packet that contains the request formatted for sending
28
28
  # a public-key request to the server.
29
- def build_request(pub_key, username, next_service, has_sig)
29
+ def build_request(pub_key, username, next_service, alg, has_sig)
30
30
  blob = Net::SSH::Buffer.new
31
31
  blob.write_key pub_key
32
32
 
33
33
  userauth_request(username, next_service, "publickey", has_sig,
34
- pub_key.ssh_type, blob.to_s)
34
+ alg, blob.to_s)
35
35
  end
36
36
 
37
37
  # Builds and sends a request formatted for a public-key
38
38
  # authentication request.
39
- def send_request(pub_key, username, next_service, signature=nil)
40
- msg = build_request(pub_key, username, next_service, !signature.nil?)
39
+ def send_request(pub_key, username, next_service, alg, signature = nil)
40
+ msg = build_request(pub_key, username, next_service, alg,
41
+ !signature.nil?)
41
42
  msg.write_string(signature) if signature
42
43
  send_message(msg)
43
44
  end
44
45
 
45
- # Attempts to perform public-key authentication for the given
46
- # username, with the given identity (public key). Returns +true+ if
47
- # successful, or +false+ otherwise.
48
- def authenticate_with(identity, next_service, username)
46
+ def authenticate_with_alg(identity, next_service, username, alg, sig_alg = nil)
49
47
  debug { "trying publickey (#{identity.fingerprint})" }
50
- send_request(identity, username, next_service)
48
+ send_request(identity, username, next_service, alg)
51
49
 
52
50
  message = session.next_message
53
51
 
54
52
  case message.type
55
53
  when USERAUTH_PK_OK
56
- buffer = build_request(identity, username, next_service, true)
54
+ buffer = build_request(identity, username, next_service, alg,
55
+ true)
57
56
  sig_data = Net::SSH::Buffer.new
58
57
  sig_data.write_string(session_id)
59
58
  sig_data.append(buffer.to_s)
60
59
 
61
- sig_blob = key_manager.sign(identity, sig_data)
60
+ sig_blob = key_manager.sign(identity, sig_data, sig_alg)
62
61
 
63
- send_request(identity, username, next_service, sig_blob.to_s)
62
+ send_request(identity, username, next_service, alg, sig_blob.to_s)
64
63
  message = session.next_message
65
64
 
66
65
  case message.type
@@ -76,7 +75,7 @@ module Net
76
75
  return false
77
76
  else
78
77
  raise Net::SSH::Exception,
79
- "unexpected server response to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
78
+ "unexpected server response to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
80
79
  end
81
80
 
82
81
  when USERAUTH_FAILURE
@@ -88,6 +87,49 @@ module Net
88
87
  raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
89
88
  end
90
89
  end
90
+
91
+ # Attempts to perform public-key authentication for the given
92
+ # username, with the given identity (public key). Returns +true+ if
93
+ # successful, or +false+ otherwise.
94
+ def authenticate_with(identity, next_service, username)
95
+ type = identity.ssh_type
96
+ if type == "ssh-rsa"
97
+ pubkey_algorithms.each do |pk_alg|
98
+ case pk_alg
99
+ when "rsa-sha2-512", "rsa-sha2-256", "ssh-rsa"
100
+ if authenticate_with_alg(identity, next_service, username, pk_alg, pk_alg)
101
+ # success
102
+ return true
103
+ end
104
+ end
105
+ end
106
+ elsif type == "ssh-rsa-cert-v01@openssh.com"
107
+ pubkey_algorithms.each do |pk_alg|
108
+ case pk_alg
109
+ when "rsa-sha2-512-cert-v01@openssh.com"
110
+ if authenticate_with_alg(identity, next_service, username, pk_alg, "rsa-sha2-512")
111
+ # success
112
+ return true
113
+ end
114
+ when "rsa-sha2-256-cert-v01@openssh.com"
115
+ if authenticate_with_alg(identity, next_service, username, pk_alg, "rsa-sha2-256")
116
+ # success
117
+ return true
118
+ end
119
+ when "ssh-rsa-cert-v01@openssh.com"
120
+ if authenticate_with_alg(identity, next_service, username, pk_alg)
121
+ # success
122
+ return true
123
+ end
124
+ end
125
+ end
126
+ elsif authenticate_with_alg(identity, next_service, username, type)
127
+ # success
128
+ return true
129
+ end
130
+ # failure
131
+ return false
132
+ end
91
133
  end
92
134
  end
93
135
  end
@@ -50,13 +50,13 @@ module Net
50
50
  SIZEOF_DWORD = DL.sizeof('L')
51
51
  elsif RUBY_VERSION < "2.1"
52
52
  extend DL::Importer
53
- dlload 'user32.dll','kernel32.dll', 'advapi32.dll'
53
+ dlload 'user32.dll', 'kernel32.dll', 'advapi32.dll'
54
54
  include DL::Win32Types
55
55
 
56
56
  SIZEOF_DWORD = DL::SIZEOF_LONG
57
57
  else
58
58
  extend Fiddle::Importer
59
- dlload 'user32.dll','kernel32.dll', 'advapi32.dll'
59
+ dlload 'user32.dll', 'kernel32.dll', 'advapi32.dll'
60
60
  include Fiddle::Win32Types
61
61
  SIZEOF_DWORD = Fiddle::SIZEOF_LONG
62
62
  end
@@ -240,7 +240,7 @@ module Net
240
240
  end
241
241
 
242
242
  def self.set_ptr_data(ptr, data)
243
- DL::CPtr.new(ptr)[0,data.size] = data
243
+ DL::CPtr.new(ptr)[0, data.size] = data
244
244
  end
245
245
  end
246
246
 
@@ -281,7 +281,7 @@ module Net
281
281
 
282
282
  def self.ptr_to_dword(ptr)
283
283
  first = ptr.ptr.to_i
284
- second = ptr_to_s(ptr,Win::SIZEOF_DWORD).unpack('L')[0]
284
+ second = ptr_to_s(ptr, Win::SIZEOF_DWORD).unpack('L')[0]
285
285
  raise "Error" unless first == second
286
286
 
287
287
  first
@@ -296,7 +296,7 @@ module Net
296
296
  end
297
297
 
298
298
  def self.get_sid(user)
299
- ptr_to_s(user.to_ptr.ptr,Win::SIZEOF_DWORD).unpack('L')[0]
299
+ ptr_to_s(user.to_ptr.ptr, Win::SIZEOF_DWORD).unpack('L')[0]
300
300
  end
301
301
 
302
302
  def self.get_sid_ptr(user)
@@ -332,7 +332,7 @@ module Net
332
332
  token_handle = open_process_token(Win.GetCurrentProcess,
333
333
  Win::TOKEN_QUERY)
334
334
  token_user = get_token_information(token_handle,
335
- Win::TOKEN_USER_INFORMATION_CLASS)
335
+ Win::TOKEN_USER_INFORMATION_CLASS)
336
336
  return token_user
337
337
  end
338
338
 
@@ -404,7 +404,7 @@ module Net
404
404
 
405
405
  if @win.to_i == 0
406
406
  raise Net::SSH::Exception,
407
- "pageant process not running"
407
+ "pageant process not running"
408
408
  end
409
409
 
410
410
  @input_buffer = Net::SSH::Buffer.new
@@ -458,7 +458,7 @@ module Net
458
458
 
459
459
  if filemap == 0 || filemap == Win::INVALID_HANDLE_VALUE
460
460
  raise Net::SSH::Exception,
461
- "Creation of file mapping failed with error: #{Win.GetLastError}"
461
+ "Creation of file mapping failed with error: #{Win.GetLastError}"
462
462
  end
463
463
 
464
464
  ptr = Win.MapViewOfFile(filemap, Win::FILE_MAP_WRITE, 0, 0,
@@ -22,12 +22,12 @@ module Net
22
22
  # returned by OpenSSH's <tt>`ssh-add -l -E SHA256`</tt>, i.e.,
23
23
  # trailing base64 padding '=' characters are stripped and the
24
24
  # literal string +SHA256:+ is prepended.
25
- def fingerprint(algorithm='MD5')
25
+ def fingerprint(algorithm = 'MD5')
26
26
  @fingerprint ||= {}
27
27
  @fingerprint[algorithm] ||= PubKeyFingerprint.fingerprint(to_blob, algorithm)
28
28
  end
29
29
 
30
- def self.fingerprint(blob, algorithm='MD5')
30
+ def self.fingerprint(blob, algorithm = 'MD5')
31
31
  case algorithm.to_s.upcase
32
32
  when 'MD5'
33
33
  OpenSSL::Digest.hexdigest(algorithm, blob).scan(/../).join(":")
@@ -41,7 +41,7 @@ module Net
41
41
 
42
42
  # Instantiates a new Authentication::Session object over the given
43
43
  # transport layer abstraction.
44
- def initialize(transport, options={})
44
+ def initialize(transport, options = {})
45
45
  self.logger = transport.logger
46
46
  @transport = transport
47
47
 
@@ -54,7 +54,7 @@ module Net
54
54
  # Attempts to authenticate the given user, in preparation for the next
55
55
  # service request. Returns true if an authentication method succeeds in
56
56
  # authenticating the user, and false otherwise.
57
- def authenticate(next_service, username, password=nil)
57
+ def authenticate(next_service, username, password = nil)
58
58
  debug { "beginning authentication of `#{username}'" }
59
59
 
60
60
  transport.send_message(transport.service_request("ssh-userauth"))
@@ -76,7 +76,9 @@ module Net
76
76
  debug { "trying #{name}" }
77
77
  begin
78
78
  auth_class = Methods.const_get(name.split(/\W+/).map { |p| p.capitalize }.join)
79
- method = auth_class.new(self, key_manager: key_manager, password_prompt: options[:password_prompt])
79
+ method = auth_class.new(self,
80
+ key_manager: key_manager, password_prompt: options[:password_prompt],
81
+ pubkey_algorithms: options[:pubkey_algorithms] || nil)
80
82
  rescue NameError
81
83
  debug {"Mechanism #{name} was requested, but isn't a known type. Ignoring it."}
82
84
  next