net-ssh 6.1.0 → 7.3.0

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 (116) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.dockerignore +6 -0
  4. data/.github/FUNDING.yml +1 -0
  5. data/.github/config/rubocop_linter_action.yml +4 -0
  6. data/.github/workflows/ci-with-docker.yml +44 -0
  7. data/.github/workflows/ci.yml +94 -0
  8. data/.github/workflows/rubocop.yml +16 -0
  9. data/.gitignore +4 -0
  10. data/.rubocop.yml +12 -1
  11. data/.rubocop_todo.yml +475 -376
  12. data/CHANGES.txt +64 -3
  13. data/DEVELOPMENT.md +23 -0
  14. data/Dockerfile +29 -0
  15. data/Dockerfile.openssl3 +17 -0
  16. data/Gemfile +2 -0
  17. data/Gemfile.noed25519 +2 -0
  18. data/Gemfile.norbnacl +12 -0
  19. data/README.md +38 -22
  20. data/Rakefile +92 -0
  21. data/SECURITY.md +4 -0
  22. data/docker-compose.yml +25 -0
  23. data/lib/net/ssh/authentication/agent.rb +29 -13
  24. data/lib/net/ssh/authentication/certificate.rb +14 -11
  25. data/lib/net/ssh/authentication/constants.rb +0 -1
  26. data/lib/net/ssh/authentication/ed25519.rb +14 -11
  27. data/lib/net/ssh/authentication/ed25519_loader.rb +4 -7
  28. data/lib/net/ssh/authentication/key_manager.rb +65 -36
  29. data/lib/net/ssh/authentication/methods/abstract.rb +12 -3
  30. data/lib/net/ssh/authentication/methods/hostbased.rb +3 -5
  31. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +2 -2
  32. data/lib/net/ssh/authentication/methods/none.rb +6 -9
  33. data/lib/net/ssh/authentication/methods/password.rb +2 -3
  34. data/lib/net/ssh/authentication/methods/publickey.rb +57 -17
  35. data/lib/net/ssh/authentication/pageant.rb +97 -97
  36. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +3 -3
  37. data/lib/net/ssh/authentication/session.rb +25 -17
  38. data/lib/net/ssh/buffer.rb +71 -51
  39. data/lib/net/ssh/buffered_io.rb +25 -26
  40. data/lib/net/ssh/config.rb +33 -20
  41. data/lib/net/ssh/connection/channel.rb +84 -82
  42. data/lib/net/ssh/connection/constants.rb +0 -4
  43. data/lib/net/ssh/connection/event_loop.rb +30 -24
  44. data/lib/net/ssh/connection/keepalive.rb +12 -12
  45. data/lib/net/ssh/connection/session.rb +109 -108
  46. data/lib/net/ssh/connection/term.rb +56 -58
  47. data/lib/net/ssh/errors.rb +12 -12
  48. data/lib/net/ssh/key_factory.rb +7 -8
  49. data/lib/net/ssh/known_hosts.rb +86 -18
  50. data/lib/net/ssh/loggable.rb +8 -9
  51. data/lib/net/ssh/packet.rb +1 -1
  52. data/lib/net/ssh/prompt.rb +9 -11
  53. data/lib/net/ssh/proxy/command.rb +1 -1
  54. data/lib/net/ssh/proxy/errors.rb +2 -4
  55. data/lib/net/ssh/proxy/http.rb +18 -20
  56. data/lib/net/ssh/proxy/https.rb +8 -10
  57. data/lib/net/ssh/proxy/jump.rb +8 -10
  58. data/lib/net/ssh/proxy/socks4.rb +2 -4
  59. data/lib/net/ssh/proxy/socks5.rb +3 -5
  60. data/lib/net/ssh/service/forward.rb +7 -7
  61. data/lib/net/ssh/test/channel.rb +24 -26
  62. data/lib/net/ssh/test/extensions.rb +35 -35
  63. data/lib/net/ssh/test/kex.rb +6 -8
  64. data/lib/net/ssh/test/local_packet.rb +0 -2
  65. data/lib/net/ssh/test/packet.rb +3 -3
  66. data/lib/net/ssh/test/remote_packet.rb +6 -8
  67. data/lib/net/ssh/test/script.rb +25 -27
  68. data/lib/net/ssh/test/socket.rb +12 -15
  69. data/lib/net/ssh/test.rb +4 -5
  70. data/lib/net/ssh/transport/aes128_gcm.rb +40 -0
  71. data/lib/net/ssh/transport/aes256_gcm.rb +40 -0
  72. data/lib/net/ssh/transport/algorithms.rb +51 -19
  73. data/lib/net/ssh/transport/chacha20_poly1305_cipher.rb +117 -0
  74. data/lib/net/ssh/transport/chacha20_poly1305_cipher_loader.rb +17 -0
  75. data/lib/net/ssh/transport/cipher_factory.rb +56 -29
  76. data/lib/net/ssh/transport/constants.rb +3 -3
  77. data/lib/net/ssh/transport/ctr.rb +7 -7
  78. data/lib/net/ssh/transport/gcm_cipher.rb +207 -0
  79. data/lib/net/ssh/transport/hmac/abstract.rb +20 -5
  80. data/lib/net/ssh/transport/hmac/md5.rb +0 -2
  81. data/lib/net/ssh/transport/hmac/md5_96.rb +0 -2
  82. data/lib/net/ssh/transport/hmac/none.rb +0 -2
  83. data/lib/net/ssh/transport/hmac/ripemd160.rb +0 -2
  84. data/lib/net/ssh/transport/hmac/sha1.rb +0 -2
  85. data/lib/net/ssh/transport/hmac/sha1_96.rb +0 -2
  86. data/lib/net/ssh/transport/hmac.rb +12 -12
  87. data/lib/net/ssh/transport/identity_cipher.rb +19 -13
  88. data/lib/net/ssh/transport/kex/abstract.rb +12 -5
  89. data/lib/net/ssh/transport/kex/abstract5656.rb +1 -1
  90. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +2 -1
  91. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +4 -4
  92. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
  93. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +21 -21
  94. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +1 -2
  95. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +2 -2
  96. data/lib/net/ssh/transport/kex.rb +8 -6
  97. data/lib/net/ssh/transport/key_expander.rb +7 -8
  98. data/lib/net/ssh/transport/openssl.rb +51 -26
  99. data/lib/net/ssh/transport/openssl_cipher_extensions.rb +8 -0
  100. data/lib/net/ssh/transport/packet_stream.rb +46 -26
  101. data/lib/net/ssh/transport/server_version.rb +17 -16
  102. data/lib/net/ssh/transport/session.rb +9 -7
  103. data/lib/net/ssh/transport/state.rb +44 -44
  104. data/lib/net/ssh/verifiers/accept_new.rb +0 -2
  105. data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +1 -2
  106. data/lib/net/ssh/verifiers/always.rb +6 -4
  107. data/lib/net/ssh/verifiers/never.rb +0 -2
  108. data/lib/net/ssh/version.rb +2 -2
  109. data/lib/net/ssh.rb +15 -8
  110. data/net-ssh-public_cert.pem +19 -18
  111. data/net-ssh.gemspec +7 -4
  112. data/support/ssh_tunnel_bug.rb +3 -3
  113. data.tar.gz.sig +0 -0
  114. metadata +76 -29
  115. metadata.gz.sig +0 -0
  116. data/.travis.yml +0 -52
@@ -3,8 +3,6 @@ gem 'bcrypt_pbkdf', '~> 1.0' unless RUBY_PLATFORM == "java"
3
3
 
4
4
  require 'ed25519'
5
5
 
6
- require 'base64'
7
-
8
6
  require 'net/ssh/transport/cipher_factory'
9
7
  require 'net/ssh/authentication/pub_key_fingerprint'
10
8
  require 'bcrypt_pbkdf' unless RUBY_PLATFORM == "java"
@@ -14,7 +12,7 @@ module Net
14
12
  module Authentication
15
13
  module ED25519
16
14
  class SigningKeyFromFile < SimpleDelegator
17
- def initialize(pk,sk)
15
+ def initialize(pk, sk)
18
16
  key = ::Ed25519::SigningKey.from_keypair(sk)
19
17
  raise ArgumentError, "pk does not match sk" unless pk == key.verify_key.to_bytes
20
18
 
@@ -44,9 +42,11 @@ module Net
44
42
  datafull = datafull.strip
45
43
  raise ArgumentError.new("Expected #{MBEGIN} at start of private key") unless datafull.start_with?(MBEGIN)
46
44
  raise ArgumentError.new("Expected #{MEND} at end of private key") unless datafull.end_with?(MEND)
45
+
47
46
  datab64 = datafull[MBEGIN.size...-MEND.size]
48
- data = Base64.decode64(datab64)
47
+ data = datab64.unpack1("m")
49
48
  raise ArgumentError.new("Expected #{MAGIC} at start of decoded private key") unless data.start_with?(MAGIC)
49
+
50
50
  buffer = Net::SSH::Buffer.new(data[MAGIC.size + 1..-1])
51
51
 
52
52
  ciphername = buffer.read_string
@@ -59,6 +59,7 @@ module Net
59
59
  kdfopts = Net::SSH::Buffer.new(buffer.read_string)
60
60
  num_keys = buffer.read_long
61
61
  raise ArgumentError.new("Only 1 key is supported in ssh keys #{num_keys} was in private key") unless num_keys == 1
62
+
62
63
  _pubkey = buffer.read_string
63
64
 
64
65
  len = buffer.read_long
@@ -72,12 +73,14 @@ module Net
72
73
  rounds = kdfopts.read_long
73
74
 
74
75
  raise "BCryptPbkdf is not implemented for jruby" if RUBY_PLATFORM == "java"
76
+
75
77
  key = BCryptPbkdf::key(password, salt, keylen + ivlen, rounds)
78
+ raise DecryptError.new("BCyryptPbkdf failed", encrypted_key: true) unless key
76
79
  else
77
80
  key = '\x00' * (keylen + ivlen)
78
81
  end
79
82
 
80
- cipher = CipherFactory.get(ciphername, key: key[0...keylen], iv:key[keylen...keylen + ivlen], decrypt: true)
83
+ cipher = CipherFactory.get(ciphername, key: key[0...keylen], iv: key[keylen...keylen + ivlen], decrypt: true)
81
84
 
82
85
  decoded = cipher.update(buffer.remainder_as_buffer.to_s)
83
86
  decoded << cipher.final
@@ -112,7 +115,7 @@ module Net
112
115
  end
113
116
 
114
117
  def to_blob
115
- Net::SSH::Buffer.from(:mstring,"ssh-ed25519",:string,@verify_key.to_bytes).to_s
118
+ Net::SSH::Buffer.from(:mstring, "ssh-ed25519".dup, :string, @verify_key.to_bytes).to_s
116
119
  end
117
120
 
118
121
  def ssh_type
@@ -123,13 +126,13 @@ module Net
123
126
  ssh_type
124
127
  end
125
128
 
126
- def ssh_do_verify(sig,data)
127
- @verify_key.verify(sig,data)
129
+ def ssh_do_verify(sig, data, options = {})
130
+ @verify_key.verify(sig, data)
128
131
  end
129
132
 
130
133
  def to_pem
131
134
  # TODO this is not pem
132
- ssh_type + Base64.encode64(@verify_key.to_bytes)
135
+ ssh_type + [@verify_key.to_bytes].pack("m")
133
136
  end
134
137
  end
135
138
 
@@ -148,7 +151,7 @@ module Net
148
151
  _comment = buffer.read_string
149
152
 
150
153
  @pk = pk
151
- @sign_key = SigningKeyFromFile.new(pk,sk)
154
+ @sign_key = SigningKeyFromFile.new(pk, sk)
152
155
  end
153
156
 
154
157
  def to_blob
@@ -167,7 +170,7 @@ module Net
167
170
  PubKey.new(@pk)
168
171
  end
169
172
 
170
- def ssh_do_sign(data)
173
+ def ssh_do_sign(data, sig_alg = nil)
171
174
  @sign_key.sign(data)
172
175
  end
173
176
 
@@ -1,11 +1,9 @@
1
- module Net
2
- module SSH
1
+ module Net
2
+ module SSH
3
3
  module Authentication
4
-
5
4
  # Loads ED25519 support which requires optinal dependecies like
6
5
  # ed25519, bcrypt_pbkdf
7
6
  module ED25519Loader
8
-
9
7
  begin
10
8
  require 'net/ssh/authentication/ed25519'
11
9
  LOADED = true
@@ -14,20 +12,19 @@ module Net
14
12
  ERROR = e
15
13
  LOADED = false
16
14
  end
17
-
15
+
18
16
  def self.raiseUnlessLoaded(message)
19
17
  description = ERROR.is_a?(LoadError) ? dependenciesRequiredForED25519 : ''
20
18
  description << "#{ERROR.class} : \"#{ERROR.message}\"\n" if ERROR
21
19
  raise NotImplementedError, "#{message}\n#{description}" unless LOADED
22
20
  end
23
-
21
+
24
22
  def self.dependenciesRequiredForED25519
25
23
  result = "net-ssh requires the following gems for ed25519 support:\n"
26
24
  result << " * ed25519 (>= 1.2, < 2.0)\n"
27
25
  result << " * bcrypt_pbkdf (>= 1.0, < 2.0)\n" unless RUBY_PLATFORM == "java"
28
26
  result << "See https://github.com/net-ssh/net-ssh/issues/565 for more information\n"
29
27
  end
30
-
31
28
  end
32
29
  end
33
30
  end
@@ -6,7 +6,6 @@ require 'net/ssh/authentication/agent'
6
6
  module Net
7
7
  module SSH
8
8
  module Authentication
9
-
10
9
  # A trivial exception class used to report errors in the key manager.
11
10
  class KeyManagerError < Net::SSH::Exception; end
12
11
 
@@ -33,6 +32,9 @@ module Net
33
32
  # The list of user key certificate files that will be examined
34
33
  attr_reader :keycert_files
35
34
 
35
+ # The list of user key certificate data that will be examined
36
+ attr_reader :keycert_data
37
+
36
38
  # The map of loaded identities
37
39
  attr_reader :known_identities
38
40
 
@@ -42,11 +44,12 @@ module Net
42
44
  # Create a new KeyManager. By default, the manager will
43
45
  # use the ssh-agent if it is running and the `:use_agent` option
44
46
  # is not false.
45
- def initialize(logger, options={})
47
+ def initialize(logger, options = {})
46
48
  self.logger = logger
47
49
  @key_files = []
48
50
  @key_data = []
49
51
  @keycert_files = []
52
+ @keycert_data = []
50
53
  @use_agent = options[:use_agent] != false
51
54
  @known_identities = {}
52
55
  @agent = nil
@@ -60,6 +63,7 @@ module Net
60
63
  def clear!
61
64
  key_files.clear
62
65
  key_data.clear
66
+ keycert_data.clear
63
67
  known_identities.clear
64
68
  self
65
69
  end
@@ -76,6 +80,12 @@ module Net
76
80
  self
77
81
  end
78
82
 
83
+ # Add the given keycert_data to the list of keycerts that will be used.
84
+ def add_keycert_data(keycert_data_)
85
+ keycert_data.push(keycert_data_).uniq!
86
+ self
87
+ end
88
+
79
89
  # Add the given key_file to the list of keys that will be used.
80
90
  def add_key_data(key_data_)
81
91
  key_data.push(key_data_).uniq!
@@ -133,8 +143,8 @@ module Net
133
143
  end
134
144
 
135
145
  known_identity_blobs = known_identities.keys.map(&:to_blob)
136
- keycert_files.each do |keycert_file|
137
- keycert = KeyFactory.load_public_key(keycert_file)
146
+
147
+ keycerts.each do |keycert|
138
148
  next if known_identity_blobs.include?(keycert.to_blob)
139
149
 
140
150
  (_, corresponding_identity) = known_identities.detect { |public_key, _|
@@ -159,7 +169,7 @@ module Net
159
169
  # Regardless of the identity's origin or who does the signing, this
160
170
  # will always return the signature in an SSH2-specified "signature
161
171
  # blob" format.
162
- def sign(identity, data)
172
+ def sign(identity, data, sig_alg = nil)
163
173
  info = known_identities[identity] or raise KeyManagerError, "the given identity is unknown to the key manager"
164
174
 
165
175
  if info[:key].nil? && info[:from] == :file
@@ -171,13 +181,27 @@ module Net
171
181
  end
172
182
 
173
183
  if info[:key]
174
- return Net::SSH::Buffer.from(:string, identity.ssh_signature_type,
175
- :mstring, info[:key].ssh_do_sign(data.to_s)).to_s
184
+ if sig_alg.nil?
185
+ signed = info[:key].ssh_do_sign(data.to_s)
186
+ sig_alg = identity.ssh_signature_type
187
+ else
188
+ signed = info[:key].ssh_do_sign(data.to_s, sig_alg)
189
+ end
190
+ return Net::SSH::Buffer.from(:string, sig_alg,
191
+ :mstring, signed).to_s
176
192
  end
177
193
 
178
194
  if info[:from] == :agent
179
195
  raise KeyManagerError, "the agent is no longer available" unless agent
180
- return agent.sign(info[:identity], data.to_s)
196
+
197
+ case sig_alg
198
+ when "rsa-sha2-512"
199
+ return agent.sign(info[:identity], data.to_s, Net::SSH::Authentication::Agent::SSH_AGENT_RSA_SHA2_512)
200
+ when "rsa-sha2-256"
201
+ return agent.sign(info[:identity], data.to_s, Net::SSH::Authentication::Agent::SSH_AGENT_RSA_SHA2_256)
202
+ else
203
+ return agent.sign(info[:identity], data.to_s)
204
+ end
181
205
  end
182
206
 
183
207
  raise KeyManagerError, "[BUG] can't determine identity origin (#{info.inspect})"
@@ -201,6 +225,7 @@ module Net
201
225
  # or if the agent is otherwise not available.
202
226
  def agent
203
227
  return unless use_agent?
228
+
204
229
  @agent ||= Agent.connect(logger, options[:agent_socket_factory], options[:identity_agent])
205
230
  rescue AgentNotAvailable
206
231
  @use_agent = false
@@ -213,6 +238,12 @@ module Net
213
238
 
214
239
  private
215
240
 
241
+ # Load keycerts from files and data.
242
+ def keycerts
243
+ keycert_files.map { |keycert_file| KeyFactory.load_public_key(keycert_file) } +
244
+ keycert_data.map { |data| KeyFactory.load_data_public_key(data) }
245
+ end
246
+
216
247
  # Prepares identities from user key_files for loading, preserving their order and sources.
217
248
  def prepare_identities_from_files
218
249
  key_files.map do |file|
@@ -248,37 +279,35 @@ module Net
248
279
  # Load prepared identities. Private key decryption errors ignored if ignore_decryption_errors
249
280
  def load_identities(identities, ask_passphrase, ignore_decryption_errors)
250
281
  identities.map do |identity|
251
- begin
252
- case identity[:load_from]
253
- when :pubkey_file
254
- key = KeyFactory.load_public_key(identity[:pubkey_file])
255
- { public_key: key, from: :file, file: identity[:privkey_file] }
256
- when :privkey_file
257
- private_key = KeyFactory.load_private_key(
258
- identity[:privkey_file], options[:passphrase], ask_passphrase, options[:password_prompt]
259
- )
260
- key = private_key.send(:public_key)
261
- { public_key: key, from: :file, file: identity[:privkey_file], key: private_key }
262
- when :data
263
- private_key = KeyFactory.load_data_private_key(
264
- identity[:data], options[:passphrase], ask_passphrase, "<key in memory>", options[:password_prompt]
265
- )
266
- key = private_key.send(:public_key)
267
- { public_key: key, from: :key_data, data: identity[:data], key: private_key }
268
- else
269
- identity
270
- end
271
- rescue OpenSSL::PKey::RSAError, OpenSSL::PKey::DSAError, OpenSSL::PKey::ECError, OpenSSL::PKey::PKeyError, ArgumentError => e
272
- if ignore_decryption_errors
273
- identity
274
- else
275
- process_identity_loading_error(identity, e)
276
- nil
277
- end
278
- rescue Exception => e
282
+ case identity[:load_from]
283
+ when :pubkey_file
284
+ key = KeyFactory.load_public_key(identity[:pubkey_file])
285
+ { public_key: key, from: :file, file: identity[:privkey_file] }
286
+ when :privkey_file
287
+ private_key = KeyFactory.load_private_key(
288
+ identity[:privkey_file], options[:passphrase], ask_passphrase, options[:password_prompt]
289
+ )
290
+ key = private_key.send(:public_key)
291
+ { public_key: key, from: :file, file: identity[:privkey_file], key: private_key }
292
+ when :data
293
+ private_key = KeyFactory.load_data_private_key(
294
+ identity[:data], options[:passphrase], ask_passphrase, "<key in memory>", options[:password_prompt]
295
+ )
296
+ key = private_key.send(:public_key)
297
+ { public_key: key, from: :key_data, data: identity[:data], key: private_key }
298
+ else
299
+ identity
300
+ end
301
+ rescue OpenSSL::PKey::RSAError, OpenSSL::PKey::DSAError, OpenSSL::PKey::ECError, OpenSSL::PKey::PKeyError, ArgumentError => e
302
+ if ignore_decryption_errors
303
+ identity
304
+ else
279
305
  process_identity_loading_error(identity, e)
280
306
  nil
281
307
  end
308
+ rescue Exception => e
309
+ process_identity_loading_error(identity, e)
310
+ nil
282
311
  end.compact
283
312
  end
284
313
 
@@ -7,7 +7,6 @@ module Net
7
7
  module SSH
8
8
  module Authentication
9
9
  module Methods
10
-
11
10
  # The base class of all user authentication methods. It provides a few
12
11
  # bits of common functionality.
13
12
  class Abstract
@@ -21,12 +20,22 @@ module Net
21
20
  # this.
22
21
  attr_reader :key_manager
23
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
+
24
28
  # Instantiates a new authentication method.
25
- def initialize(session, options={})
29
+ def initialize(session, options = {})
26
30
  @session = session
27
31
  @key_manager = options[:key_manager]
28
32
  @options = options
29
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]
30
39
  self.logger = session.logger
31
40
  end
32
41
 
@@ -47,7 +56,7 @@ module Net
47
56
  # of the packet. The new packet is returned, ready for sending.
48
57
  def userauth_request(username, next_service, auth_method, *others)
49
58
  buffer = Net::SSH::Buffer.from(:byte, USERAUTH_REQUEST,
50
- :string, username, :string, next_service, :string, auth_method)
59
+ :string, username, :string, next_service, :string, auth_method)
51
60
 
52
61
  others.each do |value|
53
62
  case value
@@ -4,19 +4,18 @@ module Net
4
4
  module SSH
5
5
  module Authentication
6
6
  module Methods
7
-
8
7
  # Implements the host-based SSH authentication method.
9
8
  class Hostbased < Abstract
10
9
  include Constants
11
10
 
12
11
  # Attempts to perform host-based authorization of the user by trying
13
12
  # all known keys.
14
- def authenticate(next_service, username, password=nil)
13
+ def authenticate(next_service, username, password = nil)
15
14
  return false unless key_manager
16
15
 
17
16
  key_manager.each_identity do |identity|
18
17
  return true if authenticate_with(identity, next_service,
19
- username, key_manager)
18
+ username, key_manager)
20
19
  end
21
20
 
22
21
  return false
@@ -64,10 +63,9 @@ module Net
64
63
  # Build the "core" hostbased request string.
65
64
  def build_request(identity, next_service, username, hostname, client_username)
66
65
  userauth_request(username, next_service, "hostbased", identity.ssh_type,
67
- Buffer.from(:key, identity).to_s, hostname, client_username).to_s
66
+ Buffer.from(:key, identity).to_s, hostname, client_username).to_s
68
67
  end
69
68
  end
70
-
71
69
  end
72
70
  end
73
71
  end
@@ -5,14 +5,13 @@ module Net
5
5
  module SSH
6
6
  module Authentication
7
7
  module Methods
8
-
9
8
  # Implements the "keyboard-interactive" SSH authentication method.
10
9
  class KeyboardInteractive < Abstract
11
10
  USERAUTH_INFO_REQUEST = 60
12
11
  USERAUTH_INFO_RESPONSE = 61
13
12
 
14
13
  # Attempt to authenticate the given user for the given service.
15
- def authenticate(next_service, username, password=nil)
14
+ def authenticate(next_service, username, password = nil)
16
15
  debug { "trying keyboard-interactive" }
17
16
  send_message(userauth_request(username, next_service, "keyboard-interactive", "", ""))
18
17
 
@@ -32,6 +31,7 @@ module Net
32
31
  message[:authentications].split(/,/).include? 'keyboard-interactive'
33
32
 
34
33
  return false unless interactive?
34
+
35
35
  password = nil
36
36
  debug { "retrying keyboard-interactive" }
37
37
  send_message(userauth_request(username, next_service, "keyboard-interactive", "", ""))
@@ -5,32 +5,29 @@ module Net
5
5
  module SSH
6
6
  module Authentication
7
7
  module Methods
8
-
9
8
  # Implements the "none" SSH authentication method.
10
9
  class None < Abstract
11
10
  # Attempt to authenticate as "none"
12
- def authenticate(next_service, user="", password="")
13
- send_message(userauth_request(user, next_service, "none"))
11
+ def authenticate(next_service, user = "", password = "")
12
+ send_message(userauth_request(user, next_service, "none"))
14
13
  message = session.next_message
15
-
14
+
16
15
  case message.type
17
16
  when USERAUTH_SUCCESS
18
17
  debug { "none succeeded" }
19
18
  return true
20
19
  when USERAUTH_FAILURE
21
20
  debug { "none failed" }
22
-
21
+
23
22
  raise Net::SSH::Authentication::DisallowedMethod unless
24
23
  message[:authentications].split(/,/).include? 'none'
25
-
24
+
26
25
  return false
27
26
  else
28
27
  raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
29
- end
30
-
28
+ end
31
29
  end
32
30
  end
33
-
34
31
  end
35
32
  end
36
33
  end
@@ -6,12 +6,11 @@ module Net
6
6
  module SSH
7
7
  module Authentication
8
8
  module Methods
9
-
10
9
  # Implements the "password" SSH authentication method.
11
10
  class Password < Abstract
12
11
  # Attempt to authenticate the given user for the given service. If
13
12
  # the password parameter is nil, this will ask for password
14
- def authenticate(next_service, username, password=nil)
13
+ def authenticate(next_service, username, password = nil)
15
14
  clear_prompter!
16
15
  retries = 0
17
16
  max_retries = get_max_retries
@@ -29,6 +28,7 @@ module Net
29
28
 
30
29
  raise Net::SSH::Authentication::DisallowedMethod unless
31
30
  message[:authentications].split(/,/).include? 'password'
31
+
32
32
  password = nil
33
33
  end
34
34
  end until (message.type != USERAUTH_FAILURE || retries >= max_retries)
@@ -74,7 +74,6 @@ module Net
74
74
  options[:non_interactive] ? 0 : result
75
75
  end
76
76
  end
77
-
78
77
  end
79
78
  end
80
79
  end
@@ -6,14 +6,13 @@ module Net
6
6
  module SSH
7
7
  module Authentication
8
8
  module Methods
9
-
10
9
  # Implements the "publickey" SSH authentication method.
11
10
  class Publickey < Abstract
12
11
  # Attempts to perform public-key authentication for the given
13
12
  # username, trying each identity known to the key manager. If any of
14
13
  # them succeed, returns +true+, otherwise returns +false+. This
15
14
  # requires the presence of a key manager.
16
- def authenticate(next_service, username, password=nil)
15
+ def authenticate(next_service, username, password = nil)
17
16
  return false unless key_manager
18
17
 
19
18
  key_manager.each_identity do |identity|
@@ -27,41 +26,40 @@ module Net
27
26
 
28
27
  # Builds a packet that contains the request formatted for sending
29
28
  # a public-key request to the server.
30
- def build_request(pub_key, username, next_service, has_sig)
29
+ def build_request(pub_key, username, next_service, alg, has_sig)
31
30
  blob = Net::SSH::Buffer.new
32
31
  blob.write_key pub_key
33
32
 
34
33
  userauth_request(username, next_service, "publickey", has_sig,
35
- pub_key.ssh_type, blob.to_s)
34
+ alg, blob.to_s)
36
35
  end
37
36
 
38
37
  # Builds and sends a request formatted for a public-key
39
38
  # authentication request.
40
- def send_request(pub_key, username, next_service, signature=nil)
41
- 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?)
42
42
  msg.write_string(signature) if signature
43
43
  send_message(msg)
44
44
  end
45
45
 
46
- # Attempts to perform public-key authentication for the given
47
- # username, with the given identity (public key). Returns +true+ if
48
- # successful, or +false+ otherwise.
49
- def authenticate_with(identity, next_service, username)
50
- debug { "trying publickey (#{identity.fingerprint})" }
51
- send_request(identity, username, next_service)
46
+ def authenticate_with_alg(identity, next_service, username, alg, sig_alg = nil)
47
+ debug { "trying publickey (#{identity.fingerprint}) alg #{alg}" }
48
+ send_request(identity, username, next_service, alg)
52
49
 
53
50
  message = session.next_message
54
51
 
55
52
  case message.type
56
53
  when USERAUTH_PK_OK
57
- buffer = build_request(identity, username, next_service, true)
54
+ buffer = build_request(identity, username, next_service, alg,
55
+ true)
58
56
  sig_data = Net::SSH::Buffer.new
59
57
  sig_data.write_string(session_id)
60
58
  sig_data.append(buffer.to_s)
61
59
 
62
- sig_blob = key_manager.sign(identity, sig_data)
60
+ sig_blob = key_manager.sign(identity, sig_data, sig_alg)
63
61
 
64
- send_request(identity, username, next_service, sig_blob.to_s)
62
+ send_request(identity, username, next_service, alg, sig_blob.to_s)
65
63
  message = session.next_message
66
64
 
67
65
  case message.type
@@ -77,7 +75,7 @@ module Net
77
75
  return false
78
76
  else
79
77
  raise Net::SSH::Exception,
80
- "unexpected server response to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
78
+ "unexpected server response to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
81
79
  end
82
80
 
83
81
  when USERAUTH_FAILURE
@@ -89,8 +87,50 @@ module Net
89
87
  raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
90
88
  end
91
89
  end
92
- end
93
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
133
+ end
94
134
  end
95
135
  end
96
136
  end