net-ssh 2.9.2 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.gitignore +6 -0
  4. data/.rubocop.yml +5 -0
  5. data/.rubocop_todo.yml +1129 -0
  6. data/.travis.yml +41 -5
  7. data/CHANGES.txt +133 -1
  8. data/Gemfile +13 -0
  9. data/Gemfile.norbnacl +10 -0
  10. data/Gemfile.norbnacl.lock +41 -0
  11. data/ISSUE_TEMPLATE.md +30 -0
  12. data/README.rdoc +26 -81
  13. data/Rakefile +63 -45
  14. data/appveyor.yml +51 -0
  15. data/lib/net/ssh/authentication/agent.rb +174 -14
  16. data/lib/net/ssh/authentication/ed25519.rb +137 -0
  17. data/lib/net/ssh/authentication/ed25519_loader.rb +21 -0
  18. data/lib/net/ssh/authentication/key_manager.rb +36 -30
  19. data/lib/net/ssh/authentication/methods/abstract.rb +4 -0
  20. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +16 -9
  21. data/lib/net/ssh/authentication/methods/password.rb +17 -4
  22. data/lib/net/ssh/authentication/pageant.rb +166 -45
  23. data/lib/net/ssh/authentication/session.rb +3 -2
  24. data/lib/net/ssh/buffer.rb +49 -10
  25. data/lib/net/ssh/buffered_io.rb +17 -12
  26. data/lib/net/ssh/config.rb +39 -8
  27. data/lib/net/ssh/connection/channel.rb +42 -20
  28. data/lib/net/ssh/connection/event_loop.rb +114 -0
  29. data/lib/net/ssh/connection/keepalive.rb +2 -2
  30. data/lib/net/ssh/connection/session.rb +120 -34
  31. data/lib/net/ssh/errors.rb +6 -6
  32. data/lib/net/ssh/key_factory.rb +49 -43
  33. data/lib/net/ssh/known_hosts.rb +49 -3
  34. data/lib/net/ssh/prompt.rb +47 -78
  35. data/lib/net/ssh/proxy/command.rb +31 -5
  36. data/lib/net/ssh/proxy/http.rb +15 -11
  37. data/lib/net/ssh/proxy/https.rb +49 -0
  38. data/lib/net/ssh/proxy/socks4.rb +2 -1
  39. data/lib/net/ssh/proxy/socks5.rb +3 -2
  40. data/lib/net/ssh/ruby_compat.rb +2 -29
  41. data/lib/net/ssh/service/forward.rb +2 -2
  42. data/lib/net/ssh/test/channel.rb +7 -0
  43. data/lib/net/ssh/test/extensions.rb +17 -0
  44. data/lib/net/ssh/test/kex.rb +4 -4
  45. data/lib/net/ssh/test/packet.rb +18 -2
  46. data/lib/net/ssh/test/script.rb +16 -2
  47. data/lib/net/ssh/test/socket.rb +1 -1
  48. data/lib/net/ssh/test.rb +5 -5
  49. data/lib/net/ssh/transport/algorithms.rb +92 -75
  50. data/lib/net/ssh/transport/cipher_factory.rb +19 -26
  51. data/lib/net/ssh/transport/ctr.rb +7 -9
  52. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +20 -9
  53. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +5 -3
  54. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +1 -1
  55. data/lib/net/ssh/transport/key_expander.rb +1 -0
  56. data/lib/net/ssh/transport/openssl.rb +1 -1
  57. data/lib/net/ssh/transport/packet_stream.rb +11 -3
  58. data/lib/net/ssh/transport/server_version.rb +13 -6
  59. data/lib/net/ssh/transport/session.rb +20 -10
  60. data/lib/net/ssh/transport/state.rb +1 -1
  61. data/lib/net/ssh/verifiers/secure.rb +8 -10
  62. data/lib/net/ssh/version.rb +4 -4
  63. data/lib/net/ssh.rb +62 -14
  64. data/net-ssh-public_cert.pem +19 -18
  65. data/net-ssh.gemspec +34 -194
  66. data/support/arcfour_check.rb +1 -1
  67. data/support/ssh_tunnel_bug.rb +1 -1
  68. data.tar.gz.sig +0 -0
  69. metadata +125 -109
  70. metadata.gz.sig +0 -0
  71. data/Rudyfile +0 -96
  72. data/lib/net/ssh/authentication/agent/java_pageant.rb +0 -85
  73. data/lib/net/ssh/authentication/agent/socket.rb +0 -178
  74. data/setup.rb +0 -1585
  75. data/test/README.txt +0 -47
  76. data/test/authentication/methods/common.rb +0 -28
  77. data/test/authentication/methods/test_abstract.rb +0 -51
  78. data/test/authentication/methods/test_hostbased.rb +0 -114
  79. data/test/authentication/methods/test_keyboard_interactive.rb +0 -100
  80. data/test/authentication/methods/test_none.rb +0 -41
  81. data/test/authentication/methods/test_password.rb +0 -95
  82. data/test/authentication/methods/test_publickey.rb +0 -148
  83. data/test/authentication/test_agent.rb +0 -224
  84. data/test/authentication/test_key_manager.rb +0 -227
  85. data/test/authentication/test_session.rb +0 -107
  86. data/test/common.rb +0 -108
  87. data/test/configs/auth_off +0 -5
  88. data/test/configs/auth_on +0 -4
  89. data/test/configs/empty +0 -0
  90. data/test/configs/eqsign +0 -3
  91. data/test/configs/exact_match +0 -8
  92. data/test/configs/host_plus +0 -10
  93. data/test/configs/multihost +0 -4
  94. data/test/configs/negative_match +0 -6
  95. data/test/configs/nohost +0 -19
  96. data/test/configs/numeric_host +0 -4
  97. data/test/configs/send_env +0 -2
  98. data/test/configs/substitutes +0 -8
  99. data/test/configs/wild_cards +0 -14
  100. data/test/connection/test_channel.rb +0 -467
  101. data/test/connection/test_session.rb +0 -543
  102. data/test/known_hosts/github +0 -1
  103. data/test/manual/test_forward.rb +0 -285
  104. data/test/manual/test_pageant.rb +0 -37
  105. data/test/start/test_connection.rb +0 -53
  106. data/test/start/test_options.rb +0 -43
  107. data/test/start/test_transport.rb +0 -28
  108. data/test/test_all.rb +0 -11
  109. data/test/test_buffer.rb +0 -433
  110. data/test/test_buffered_io.rb +0 -63
  111. data/test/test_config.rb +0 -221
  112. data/test/test_key_factory.rb +0 -191
  113. data/test/test_known_hosts.rb +0 -13
  114. data/test/transport/hmac/test_md5.rb +0 -41
  115. data/test/transport/hmac/test_md5_96.rb +0 -27
  116. data/test/transport/hmac/test_none.rb +0 -34
  117. data/test/transport/hmac/test_ripemd160.rb +0 -36
  118. data/test/transport/hmac/test_sha1.rb +0 -36
  119. data/test/transport/hmac/test_sha1_96.rb +0 -27
  120. data/test/transport/hmac/test_sha2_256.rb +0 -37
  121. data/test/transport/hmac/test_sha2_256_96.rb +0 -27
  122. data/test/transport/hmac/test_sha2_512.rb +0 -37
  123. data/test/transport/hmac/test_sha2_512_96.rb +0 -27
  124. data/test/transport/kex/test_diffie_hellman_group14_sha1.rb +0 -13
  125. data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +0 -146
  126. data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +0 -92
  127. data/test/transport/kex/test_diffie_hellman_group_exchange_sha256.rb +0 -34
  128. data/test/transport/kex/test_ecdh_sha2_nistp256.rb +0 -161
  129. data/test/transport/kex/test_ecdh_sha2_nistp384.rb +0 -38
  130. data/test/transport/kex/test_ecdh_sha2_nistp521.rb +0 -38
  131. data/test/transport/test_algorithms.rb +0 -324
  132. data/test/transport/test_cipher_factory.rb +0 -443
  133. data/test/transport/test_hmac.rb +0 -34
  134. data/test/transport/test_identity_cipher.rb +0 -40
  135. data/test/transport/test_packet_stream.rb +0 -1761
  136. data/test/transport/test_server_version.rb +0 -78
  137. data/test/transport/test_session.rb +0 -331
  138. data/test/transport/test_state.rb +0 -181
@@ -31,10 +31,10 @@ module Net; module SSH; module Test
31
31
  buffer = @connection.next_message
32
32
  raise Net::SSH::Exception, "expected NEWKEYS" unless buffer.type == NEWKEYS
33
33
 
34
- { :session_id => "abc-xyz",
35
- :server_key => OpenSSL::PKey::RSA.new(512),
36
- :shared_secret => OpenSSL::BN.new("1234567890", 10),
37
- :hashing_algorithm => OpenSSL::Digest::SHA1 }
34
+ { session_id: "abc-xyz",
35
+ server_key: OpenSSL::PKey::RSA.new(512),
36
+ shared_secret: OpenSSL::BN.new("1234567890", 10),
37
+ hashing_algorithm: OpenSSL::Digest::SHA1 }
38
38
  end
39
39
  end
40
40
 
@@ -17,6 +17,17 @@ module Net; module SSH; module Test
17
17
  include Net::SSH::Transport::Constants
18
18
  include Net::SSH::Connection::Constants
19
19
 
20
+ # Register a custom channel request. extra_parts is an array of types
21
+ # of extra parameters
22
+ def self.register_channel_request(request, extra_parts)
23
+ @registered_requests ||= {}
24
+ @registered_requests[request] = {extra_parts: extra_parts}
25
+ end
26
+
27
+ def self.registered_channel_requests(request)
28
+ @registered_requests && @registered_requests[request]
29
+ end
30
+
20
31
  # Ceate a new packet of the given +type+, and with +args+ being a list of
21
32
  # data elements in the order expected for packets of the given +type+
22
33
  # (see #types).
@@ -70,9 +81,14 @@ module Net; module SSH; module Test
70
81
  when CHANNEL_REQUEST
71
82
  parts = [:long, :string, :bool]
72
83
  case @data[1]
73
- when "exec", "subsystem" then parts << :string
84
+ when "exec", "subsystem","shell" then parts << :string
74
85
  when "exit-status" then parts << :long
75
- else raise "don't know what to do about #{@data[1]} channel request"
86
+ when "pty-req" then parts.concat([:string, :long, :long, :long, :long, :string])
87
+ when "env" then parts.contact([:string,:string])
88
+ else
89
+ request = Packet.registered_channel_requests(@data[1])
90
+ raise "don't know what to do about #{@data[1]} channel request" unless request
91
+ parts.concat(request[:extra_parts])
76
92
  end
77
93
  else raise "don't know how to parse packet type #{@type}"
78
94
  end
@@ -63,7 +63,8 @@ module Net; module SSH; module Test
63
63
  # indicating whether a response to this packet is required , and +data+
64
64
  # is any additional request-specific data that this packet should send.
65
65
  # +success+ indicates whether the response (if one is required) should be
66
- # success or failure.
66
+ # success or failure. If +data+ is an array it will be treated as multiple
67
+ # data.
67
68
  #
68
69
  # If a reply is desired, a remote packet will also be queued, :channel_success
69
70
  # if +success+ is true, or :channel_failure if +success+ is false.
@@ -71,7 +72,11 @@ module Net; module SSH; module Test
71
72
  # This will typically be called via Net::SSH::Test::Channel#sends_exec or
72
73
  # Net::SSH::Test::Channel#sends_subsystem.
73
74
  def sends_channel_request(channel, request, reply, data, success=true)
74
- events << LocalPacket.new(:channel_request, channel.remote_id, request, reply, data)
75
+ if data.is_a? Array
76
+ events << LocalPacket.new(:channel_request, channel.remote_id, request, reply, *data)
77
+ else
78
+ events << LocalPacket.new(:channel_request, channel.remote_id, request, reply, data)
79
+ end
75
80
  if reply
76
81
  if success
77
82
  events << RemotePacket.new(:channel_success, channel.local_id)
@@ -104,6 +109,15 @@ module Net; module SSH; module Test
104
109
  events << LocalPacket.new(:channel_close, channel.remote_id)
105
110
  end
106
111
 
112
+ # Scripts the sending of a channel request pty packets from the given
113
+ # Net::SSH::Test::Channel +channel+. This will typically be called via
114
+ # Net::SSH::Test::Channel#sends_request_pty.
115
+ def sends_channel_request_pty(channel)
116
+ data = ['pty-req', false]
117
+ data += Net::SSH::Connection::Channel::VALID_PTY_OPTIONS.merge(modes: "\0").values
118
+ events << LocalPacket.new(:channel_request, channel.remote_id, *data)
119
+ end
120
+
107
121
  # Scripts the reception of a channel data packet from the remote host by
108
122
  # the given Net::SSH::Test::Channel +channel+. This will typically be
109
123
  # called via Net::SSH::Test::Channel#gets_data.
@@ -25,8 +25,8 @@ module Net; module SSH; module Test
25
25
 
26
26
  @script = Script.new
27
27
 
28
- script.gets(:kexinit, 1, 2, 3, 4, "test", "ssh-rsa", "none", "none", "none", "none", "none", "none", "", "", false)
29
28
  script.sends(:kexinit)
29
+ script.gets(:kexinit, 1, 2, 3, 4, "test", "ssh-rsa", "none", "none", "none", "none", "none", "none", "", "", false)
30
30
  script.sends(:newkeys)
31
31
  script.gets(:newkeys)
32
32
  end
data/lib/net/ssh/test.rb CHANGED
@@ -10,10 +10,10 @@ module Net; module SSH
10
10
  # typically include this module in your unit test class, and then build a
11
11
  # "story" of expected sends and receives:
12
12
  #
13
- # require 'test/unit'
13
+ # require 'minitest/autorun'
14
14
  # require 'net/ssh/test'
15
15
  #
16
- # class MyTest < Test::Unit::TestCase
16
+ # class MyTest < Minitest::Test
17
17
  # include Net::SSH::Test
18
18
  #
19
19
  # def test_exec_via_channel_works
@@ -50,7 +50,7 @@ module Net; module SSH
50
50
  # If a block is given, yields the script for the test socket (#socket).
51
51
  # Otherwise, simply returns the socket's script. See Net::SSH::Test::Script.
52
52
  def story
53
- yield socket.script if block_given?
53
+ Net::SSH::Test::Extensions::IO.with_test_extension { yield socket.script if block_given? }
54
54
  return socket.script
55
55
  end
56
56
 
@@ -71,7 +71,7 @@ module Net; module SSH
71
71
  # in these tests. It is a fully functional SSH transport session, operating
72
72
  # over a mock socket (#socket).
73
73
  def transport(options={})
74
- @transport ||= Net::SSH::Transport::Session.new(options[:host] || "localhost", options.merge(:kex => "test", :host_key => "ssh-rsa", :paranoid => false, :proxy => socket(options)))
74
+ @transport ||= Net::SSH::Transport::Session.new(options[:host] || "localhost", options.merge(kex: "test", host_key: "ssh-rsa", paranoid: false, proxy: socket(options)))
75
75
  end
76
76
 
77
77
  # First asserts that a story has been described (see #story). Then yields,
@@ -81,7 +81,7 @@ module Net; module SSH
81
81
  # the block passed to this assertion.
82
82
  def assert_scripted
83
83
  raise "there is no script to be processed" if socket.script.events.empty?
84
- yield
84
+ Net::SSH::Test::Extensions::IO.with_test_extension { yield }
85
85
  assert socket.script.events.empty?, "there should not be any remaining scripted events, but there are still #{socket.script.events.length} pending"
86
86
  end
87
87
  end
@@ -6,6 +6,7 @@ require 'net/ssh/transport/constants'
6
6
  require 'net/ssh/transport/hmac'
7
7
  require 'net/ssh/transport/kex'
8
8
  require 'net/ssh/transport/server_version'
9
+ require 'net/ssh/authentication/ed25519_loader'
9
10
 
10
11
  module Net; module SSH; module Transport
11
12
 
@@ -22,40 +23,34 @@ module Net; module SSH; module Transport
22
23
  # Define the default algorithms, in order of preference, supported by
23
24
  # Net::SSH.
24
25
  ALGORITHMS = {
25
- :host_key => %w(ssh-rsa ssh-dss
26
- ssh-rsa-cert-v01@openssh.com
27
- ssh-rsa-cert-v00@openssh.com),
28
- :kex => %w(diffie-hellman-group-exchange-sha1
29
- diffie-hellman-group1-sha1
30
- diffie-hellman-group14-sha1
31
- diffie-hellman-group-exchange-sha256),
32
- :encryption => %w(aes128-cbc 3des-cbc blowfish-cbc cast128-cbc
33
- aes192-cbc aes256-cbc rijndael-cbc@lysator.liu.se
34
- idea-cbc none arcfour128 arcfour256 arcfour
35
- aes128-ctr aes192-ctr aes256-ctr
36
- camellia128-cbc camellia192-cbc camellia256-cbc
37
- camellia128-cbc@openssh.org
38
- camellia192-cbc@openssh.org
39
- camellia256-cbc@openssh.org
40
- camellia128-ctr camellia192-ctr camellia256-ctr
41
- camellia128-ctr@openssh.org
42
- camellia192-ctr@openssh.org
43
- camellia256-ctr@openssh.org
44
- cast128-ctr blowfish-ctr 3des-ctr
45
- ),
46
-
47
- :hmac => %w(hmac-sha1 hmac-md5 hmac-sha1-96 hmac-md5-96
48
- hmac-ripemd160 hmac-ripemd160@openssh.com
49
- hmac-sha2-256 hmac-sha2-512 hmac-sha2-256-96
50
- hmac-sha2-512-96 none),
51
-
52
- :compression => %w(none zlib@openssh.com zlib),
53
- :language => %w()
26
+ host_key: %w(ssh-rsa ssh-dss
27
+ ssh-rsa-cert-v01@openssh.com
28
+ ssh-rsa-cert-v00@openssh.com),
29
+ kex: %w(diffie-hellman-group-exchange-sha1
30
+ diffie-hellman-group1-sha1
31
+ diffie-hellman-group14-sha1
32
+ diffie-hellman-group-exchange-sha256),
33
+ encryption: %w(aes128-cbc 3des-cbc blowfish-cbc cast128-cbc
34
+ aes192-cbc aes256-cbc rijndael-cbc@lysator.liu.se
35
+ idea-cbc none arcfour128 arcfour256 arcfour
36
+ aes128-ctr aes192-ctr aes256-ctr
37
+ cast128-ctr blowfish-ctr 3des-ctr),
38
+
39
+ hmac: %w(hmac-sha1 hmac-md5 hmac-sha1-96 hmac-md5-96
40
+ hmac-ripemd160 hmac-ripemd160@openssh.com
41
+ hmac-sha2-256 hmac-sha2-512 hmac-sha2-256-96
42
+ hmac-sha2-512-96 none),
43
+
44
+ compression: %w(none zlib@openssh.com zlib),
45
+ language: %w()
54
46
  }
55
47
  if defined?(OpenSSL::PKey::EC)
56
48
  ALGORITHMS[:host_key] += %w(ecdsa-sha2-nistp256
57
49
  ecdsa-sha2-nistp384
58
50
  ecdsa-sha2-nistp521)
51
+ if Net::SSH::Authentication::ED25519Loader::LOADED
52
+ ALGORITHMS[:host_key] += %w(ssh-ed25519)
53
+ end
59
54
  ALGORITHMS[:kex] += %w(ecdh-sha2-nistp256
60
55
  ecdh-sha2-nistp384
61
56
  ecdh-sha2-nistp521)
@@ -124,6 +119,12 @@ module Net; module SSH; module Transport
124
119
  prepare_preferred_algorithms!
125
120
  end
126
121
 
122
+ # Start the algorithm negotation
123
+ def start
124
+ raise ArgumentError, "Cannot call start if it's negoitation started or done" if @pending || @initialized
125
+ send_kexinit
126
+ end
127
+
127
128
  # Request a rekey operation. This will return immediately, and does not
128
129
  # actually perform the rekey operation. It does cause the session to change
129
130
  # state, however--until the key exchange finishes, no new packets will be
@@ -210,25 +211,8 @@ module Net; module SSH; module Transport
210
211
  def prepare_preferred_algorithms!
211
212
  options[:compression] = %w(zlib@openssh.com zlib) if options[:compression] == true
212
213
 
213
- ALGORITHMS.each do |algorithm, list|
214
- algorithms[algorithm] = list.dup
215
-
216
- # apply the preferred algorithm order, if any
217
- if options[algorithm]
218
- algorithms[algorithm] = Array(options[algorithm]).compact.uniq
219
- unsupported = []
220
- algorithms[algorithm].select! do |name|
221
- supported = ALGORITHMS[algorithm].include?(name)
222
- unsupported << name unless supported
223
- supported
224
- end
225
- lwarn { "unsupported #{algorithm} algorithm: `#{unsupported}'" } unless unsupported.empty?
226
-
227
- # make sure all of our supported algorithms are tacked onto the
228
- # end, so that if the user tries to give a list of which none are
229
- # supported, we can still proceed.
230
- list.each { |name| algorithms[algorithm] << name unless algorithms[algorithm].include?(name) }
231
- end
214
+ ALGORITHMS.each do |algorithm, supported|
215
+ algorithms[algorithm] = compose_algorithm_list(supported, options[algorithm], options[:append_all_supported_algorithms])
232
216
  end
233
217
 
234
218
  # for convention, make sure our list has the same keys as the server
@@ -243,7 +227,7 @@ module Net; module SSH; module Transport
243
227
  # make sure the host keys are specified in preference order, where any
244
228
  # existing known key for the host has preference.
245
229
 
246
- existing_keys = KnownHosts.search_for(options[:host_key_alias] || session.host_as_string, options)
230
+ existing_keys = session.host_keys
247
231
  host_keys = existing_keys.map { |key| key.ssh_type }.uniq
248
232
  algorithms[:host_key].each do |name|
249
233
  host_keys << name unless host_keys.include?(name)
@@ -252,9 +236,41 @@ module Net; module SSH; module Transport
252
236
  end
253
237
  end
254
238
 
239
+ # Composes the list of algorithms by taking supported algorithms and matching with supplied options.
240
+ def compose_algorithm_list(supported, option, append_all_supported_algorithms = false)
241
+ return supported.dup unless option
242
+
243
+ list = []
244
+ option = Array(option).compact.uniq
245
+
246
+ if option.first && option.first.start_with?('+')
247
+ list = supported.dup
248
+ list << option.first[1..-1]
249
+ list.concat(option[1..-1])
250
+ list.uniq!
251
+ else
252
+ list = option
253
+
254
+ if append_all_supported_algorithms
255
+ supported.each { |name| list << name unless list.include?(name) }
256
+ end
257
+ end
258
+
259
+ unsupported = []
260
+ list.select! do |name|
261
+ is_supported = supported.include?(name)
262
+ unsupported << name unless is_supported
263
+ is_supported
264
+ end
265
+
266
+ lwarn { "unsupported #{algorithm} algorithm: `#{unsupported}'" } unless unsupported.empty?
267
+
268
+ list
269
+ end
270
+
255
271
  # Parses a KEXINIT packet from the server.
256
272
  def parse_server_algorithm_packet(packet)
257
- data = { :raw => packet.content }
273
+ data = { raw: packet.content }
258
274
 
259
275
  packet.read(16) # skip the cookie value
260
276
 
@@ -290,8 +306,8 @@ module Net; module SSH; module Transport
290
306
 
291
307
  Net::SSH::Buffer.from(:byte, KEXINIT,
292
308
  :long, [rand(0xFFFFFFFF), rand(0xFFFFFFFF), rand(0xFFFFFFFF), rand(0xFFFFFFFF)],
293
- :string, [kex, host_key, encryption, encryption, hmac, hmac],
294
- :string, [compression, compression, language, language],
309
+ :mstring, [kex, host_key, encryption, encryption, hmac, hmac],
310
+ :mstring, [compression, compression, language, language],
295
311
  :bool, false, :long, 0)
296
312
  end
297
313
 
@@ -355,12 +371,13 @@ module Net; module SSH; module Transport
355
371
  debug { "exchanging keys" }
356
372
 
357
373
  algorithm = Kex::MAP[kex].new(self, session,
358
- :client_version_string => Net::SSH::Transport::ServerVersion::PROTO_VERSION,
359
- :server_version_string => session.server_version.version,
360
- :server_algorithm_packet => @server_packet,
361
- :client_algorithm_packet => @client_packet,
362
- :need_bytes => kex_byte_requirement,
363
- :logger => logger)
374
+ client_version_string: Net::SSH::Transport::ServerVersion::PROTO_VERSION,
375
+ server_version_string: session.server_version.version,
376
+ server_algorithm_packet: @server_packet,
377
+ client_algorithm_packet: @client_packet,
378
+ need_bytes: kex_byte_requirement,
379
+ minimum_dh_bits: options[:minimum_dh_bits],
380
+ logger: logger)
364
381
  result = algorithm.exchange_keys
365
382
 
366
383
  secret = result[:shared_secret].to_ssh
@@ -370,7 +387,7 @@ module Net; module SSH; module Transport
370
387
  @session_id ||= hash
371
388
 
372
389
  key = Proc.new { |salt| digester.digest(secret + hash + salt + @session_id) }
373
-
390
+
374
391
  iv_client = key["A"]
375
392
  iv_server = key["B"]
376
393
  key_client = key["C"]
@@ -378,26 +395,26 @@ module Net; module SSH; module Transport
378
395
  mac_key_client = key["E"]
379
396
  mac_key_server = key["F"]
380
397
 
381
- parameters = { :shared => secret, :hash => hash, :digester => digester }
382
-
383
- cipher_client = CipherFactory.get(encryption_client, parameters.merge(:iv => iv_client, :key => key_client, :encrypt => true))
384
- cipher_server = CipherFactory.get(encryption_server, parameters.merge(:iv => iv_server, :key => key_server, :decrypt => true))
398
+ parameters = { shared: secret, hash: hash, digester: digester }
399
+
400
+ cipher_client = CipherFactory.get(encryption_client, parameters.merge(iv: iv_client, key: key_client, encrypt: true))
401
+ cipher_server = CipherFactory.get(encryption_server, parameters.merge(iv: iv_server, key: key_server, decrypt: true))
385
402
 
386
403
  mac_client = HMAC.get(hmac_client, mac_key_client, parameters)
387
404
  mac_server = HMAC.get(hmac_server, mac_key_server, parameters)
388
405
 
389
- session.configure_client :cipher => cipher_client, :hmac => mac_client,
390
- :compression => normalize_compression_name(compression_client),
391
- :compression_level => options[:compression_level],
392
- :rekey_limit => options[:rekey_limit],
393
- :max_packets => options[:rekey_packet_limit],
394
- :max_blocks => options[:rekey_blocks_limit]
395
-
396
- session.configure_server :cipher => cipher_server, :hmac => mac_server,
397
- :compression => normalize_compression_name(compression_server),
398
- :rekey_limit => options[:rekey_limit],
399
- :max_packets => options[:rekey_packet_limit],
400
- :max_blocks => options[:rekey_blocks_limit]
406
+ session.configure_client cipher: cipher_client, hmac: mac_client,
407
+ compression: normalize_compression_name(compression_client),
408
+ compression_level: options[:compression_level],
409
+ rekey_limit: options[:rekey_limit],
410
+ max_packets: options[:rekey_packet_limit],
411
+ max_blocks: options[:rekey_blocks_limit]
412
+
413
+ session.configure_server cipher: cipher_server, hmac: mac_server,
414
+ compression: normalize_compression_name(compression_server),
415
+ rekey_limit: options[:rekey_limit],
416
+ max_packets: options[:rekey_packet_limit],
417
+ max_blocks: options[:rekey_blocks_limit]
401
418
 
402
419
  @initialized = true
403
420
  end
@@ -21,12 +21,6 @@ module Net; module SSH; module Transport
21
21
  "arcfour256" => "rc4",
22
22
  "arcfour512" => "rc4",
23
23
  "arcfour" => "rc4",
24
- "camellia128-cbc" => "camellia-128-cbc",
25
- "camellia192-cbc" => "camellia-192-cbc",
26
- "camellia256-cbc" => "camellia-256-cbc",
27
- "camellia128-cbc@openssh.org" => "camellia-128-cbc",
28
- "camellia192-cbc@openssh.org" => "camellia-192-cbc",
29
- "camellia256-cbc@openssh.org" => "camellia-256-cbc",
30
24
 
31
25
  "3des-ctr" => "des-ede3",
32
26
  "blowfish-ctr" => "bf-ecb",
@@ -34,19 +28,13 @@ module Net; module SSH; module Transport
34
28
  "aes192-ctr" => "aes-192-ecb",
35
29
  "aes128-ctr" => "aes-128-ecb",
36
30
  "cast128-ctr" => "cast5-ecb",
37
- "camellia128-ctr" => "camellia-128-ecb",
38
- "camellia192-ctr" => "camellia-192-ecb",
39
- "camellia256-ctr" => "camellia-256-ecb",
40
- "camellia128-ctr@openssh.org" => "camellia-128-ecb",
41
- "camellia192-ctr@openssh.org" => "camellia-192-ecb",
42
- "camellia256-ctr@openssh.org" => "camellia-256-ecb",
43
31
 
44
32
  "none" => "none",
45
33
  }
46
34
 
47
35
  # Ruby's OpenSSL bindings always return a key length of 16 for RC4 ciphers
48
- # resulting in the error: OpenSSL::CipherError: key length too short.
49
- # The following ciphers will override this key length.
36
+ # resulting in the error: OpenSSL::CipherError: key length too short.
37
+ # The following ciphers will override this key length.
50
38
  KEY_LEN_OVERRIDE = {
51
39
  "arcfour256" => 32,
52
40
  "arcfour512" => 64
@@ -69,19 +57,18 @@ module Net; module SSH; module Transport
69
57
  def self.get(name, options={})
70
58
  ossl_name = SSH_TO_OSSL[name] or raise NotImplementedError, "unimplemented cipher `#{name}'"
71
59
  return IdentityCipher if ossl_name == "none"
72
- cipher = OpenSSL::Cipher::Cipher.new(ossl_name)
60
+ cipher = OpenSSL::Cipher.new(ossl_name)
73
61
 
74
62
  cipher.send(options[:encrypt] ? :encrypt : :decrypt)
75
63
 
76
64
  cipher.padding = 0
77
65
 
78
66
  cipher.extend(Net::SSH::Transport::CTR) if (name =~ /-ctr(@openssh.org)?$/)
79
-
80
- cipher.iv = Net::SSH::Transport::KeyExpander.expand_key(cipher.iv_len, options[:iv], options) if ossl_name != "rc4"
67
+ cipher.iv = Net::SSH::Transport::KeyExpander.expand_key(cipher.iv_len, options[:iv], options) if ossl_name != "rc4"
81
68
 
82
69
  key_len = KEY_LEN_OVERRIDE[name] || cipher.key_len
83
70
  cipher.key_len = key_len
84
- cipher.key = Net::SSH::Transport::KeyExpander.expand_key(key_len, options[:key], options)
71
+ cipher.key = Net::SSH::Transport::KeyExpander.expand_key(key_len, options[:key], options)
85
72
  cipher.update(" " * 1536) if (ossl_name == "rc4" && name != "arcfour")
86
73
 
87
74
  return cipher
@@ -91,15 +78,21 @@ module Net; module SSH; module Transport
91
78
  # block-size ] for the named cipher algorithm. If the cipher
92
79
  # algorithm is unknown, or is "none", 0 is returned for both elements
93
80
  # of the tuple.
94
- def self.get_lengths(name)
81
+ # if :iv_len option is supplied the third return value will be ivlen
82
+ def self.get_lengths(name, options = {})
95
83
  ossl_name = SSH_TO_OSSL[name]
96
- return [0, 0] if ossl_name.nil? || ossl_name == "none"
97
-
98
- cipher = OpenSSL::Cipher::Cipher.new(ossl_name)
99
- key_len = KEY_LEN_OVERRIDE[name] || cipher.key_len
100
- cipher.key_len = key_len
101
-
102
- return [key_len, ossl_name=="rc4" ? 8 : cipher.block_size]
84
+ if ossl_name.nil? || ossl_name == "none"
85
+ result = [0, 0]
86
+ result << 0 if options[:iv_len]
87
+ else
88
+ cipher = OpenSSL::Cipher.new(ossl_name)
89
+ key_len = KEY_LEN_OVERRIDE[name] || cipher.key_len
90
+ cipher.key_len = key_len
91
+
92
+ result = [key_len, ossl_name=="rc4" ? 8 : cipher.block_size]
93
+ result << cipher.iv_len if options[:iv_len]
94
+ end
95
+ result
103
96
  end
104
97
  end
105
98
 
@@ -12,12 +12,10 @@ module Net::SSH::Transport
12
12
  @counter_len = orig.block_size
13
13
  orig.encrypt
14
14
  orig.padding = 0
15
- }
16
-
17
- class <<orig
18
- alias :_update :update
19
- private :_update
20
- undef :update
15
+
16
+ singleton_class.send(:alias_method, :_update, :update)
17
+ singleton_class.send(:private, :_update)
18
+ singleton_class.send(:undef_method, :update)
21
19
 
22
20
  def iv
23
21
  @counter
@@ -73,13 +71,12 @@ module Net::SSH::Transport
73
71
  s
74
72
  end
75
73
 
76
- private
77
-
78
74
  def xor!(s1, s2)
79
75
  s = []
80
76
  s1.unpack('Q*').zip(s2.unpack('Q*')) {|a,b| s.push(a^b) }
81
77
  s.pack('Q*')
82
78
  end
79
+ singleton_class.send(:private, :xor!)
83
80
 
84
81
  def increment_counter!
85
82
  c = @counter_len
@@ -89,7 +86,8 @@ module Net::SSH::Transport
89
86
  end
90
87
  end
91
88
  end
92
- end
89
+ singleton_class.send(:private, :increment_counter!)
90
+ }
93
91
  end
94
92
  end
95
93
  end
@@ -69,10 +69,10 @@ module Net; module SSH; module Transport; module Kex
69
69
  session_id = verify_signature(result)
70
70
  confirm_newkeys
71
71
 
72
- return { :session_id => session_id,
73
- :server_key => result[:server_key],
74
- :shared_secret => result[:shared_secret],
75
- :hashing_algorithm => digester }
72
+ return { session_id: session_id,
73
+ server_key: result[:server_key],
74
+ shared_secret: result[:shared_secret],
75
+ hashing_algorithm: digester }
76
76
  end
77
77
 
78
78
  private
@@ -115,11 +115,22 @@ module Net; module SSH; module Transport; module Kex
115
115
  def generate_key #:nodoc:
116
116
  dh = OpenSSL::PKey::DH.new
117
117
 
118
- dh.p, dh.g = get_parameters
119
- dh.priv_key = OpenSSL::BN.rand(data[:need_bytes] * 8)
120
-
121
- dh.generate_key! until dh.valid?
118
+ if dh.respond_to?(:set_pqg)
119
+ p, g = get_parameters
120
+ dh.set_pqg(p, nil, g)
121
+ else
122
+ dh.p, dh.g = get_parameters
123
+ end
122
124
 
125
+ dh.generate_key!
126
+ until dh.valid? && dh.priv_key.num_bytes == data[:need_bytes]
127
+ if dh.respond_to?(:set_key)
128
+ dh.set_key(nil, OpenSSL::BN.rand(data[:need_bytes] * 8))
129
+ else
130
+ dh.priv_key = OpenSSL::BN.rand(data[:need_bytes] * 8)
131
+ end
132
+ dh.generate_key!
133
+ end
123
134
  dh
124
135
  end
125
136
 
@@ -170,7 +181,7 @@ module Net; module SSH; module Transport; module Kex
170
181
 
171
182
  blob, fingerprint = generate_key_fingerprint(key)
172
183
 
173
- unless connection.host_key_verifier.verify(:key => key, :key_blob => blob, :fingerprint => fingerprint, :session => connection)
184
+ unless connection.host_key_verifier.verify(key: key, key_blob: blob, fingerprint: fingerprint, session: connection)
174
185
  raise Net::SSH::Exception, "host key verification failed"
175
186
  end
176
187
  end
@@ -23,8 +23,10 @@ module Net::SSH::Transport::Kex
23
23
  # for Compatibility: OpenSSH requires (need_bits * 2 + 1) length of parameter
24
24
  need_bits = data[:need_bytes] * 8 * 2 + 1
25
25
 
26
- if need_bits < MINIMUM_BITS
27
- need_bits = MINIMUM_BITS
26
+ data[:minimum_dh_bits] ||= MINIMUM_BITS
27
+
28
+ if need_bits < data[:minimum_dh_bits]
29
+ need_bits = data[:minimum_dh_bits]
28
30
  elsif need_bits > MAXIMUM_BITS
29
31
  need_bits = MAXIMUM_BITS
30
32
  end
@@ -38,7 +40,7 @@ module Net::SSH::Transport::Kex
38
40
  compute_need_bits
39
41
 
40
42
  # request the DH key parameters for the given number of bits.
41
- buffer = Net::SSH::Buffer.from(:byte, KEXDH_GEX_REQUEST, :long, MINIMUM_BITS,
43
+ buffer = Net::SSH::Buffer.from(:byte, KEXDH_GEX_REQUEST, :long, data[:minimum_dh_bits],
42
44
  :long, data[:need_bits], :long, MAXIMUM_BITS)
43
45
  connection.send_message(buffer)
44
46
 
@@ -57,7 +57,7 @@ module Net; module SSH; module Transport; module Kex
57
57
  # send the KEXECDH_INIT message
58
58
  ## byte SSH_MSG_KEX_ECDH_INIT
59
59
  ## string Q_C, client's ephemeral public key octet string
60
- buffer = Net::SSH::Buffer.from(:byte, init, :string, ecdh.public_key.to_bn.to_s(2))
60
+ buffer = Net::SSH::Buffer.from(:byte, init, :mstring, ecdh.public_key.to_bn.to_s(2))
61
61
  connection.send_message(buffer)
62
62
 
63
63
  # expect the following KEXECDH_REPLY message
@@ -9,6 +9,7 @@ module Net; module SSH; module Transport
9
9
  end
10
10
 
11
11
  k = start[0, bytes]
12
+ return k if k.length >= bytes
12
13
 
13
14
  digester = options[:digester] or raise 'No digester supplied'
14
15
  shared = options[:shared] or raise 'No shared secret supplied'
@@ -185,7 +185,7 @@ module OpenSSL
185
185
  def to_blob
186
186
  @blob ||= Net::SSH::Buffer.from(:string, ssh_type,
187
187
  :string, CurveNameAliasInv[self.group.curve_name],
188
- :string, self.public_key.to_bn.to_s(2)).to_s
188
+ :mstring, self.public_key.to_bn.to_s(2)).to_s
189
189
  @blob
190
190
  end
191
191
 
@@ -69,13 +69,13 @@ module Net; module SSH; module Transport
69
69
  PROXY_COMMAND_HOST_IP
70
70
  end
71
71
  end
72
-
72
+
73
73
  # Returns true if the IO is available for reading, and false otherwise.
74
74
  def available_for_read?
75
75
  result = Net::SSH::Compat.io_select([self], nil, nil, 0)
76
76
  result && result.first.any?
77
77
  end
78
-
78
+
79
79
  # Returns the next full packet. If the mode parameter is :nonblock (the
80
80
  # default), then this will return immediately, whether a packet is
81
81
  # available or not, and will return nil if there is no packet ready to be
@@ -84,9 +84,17 @@ module Net; module SSH; module Transport
84
84
  def next_packet(mode=:nonblock)
85
85
  case mode
86
86
  when :nonblock then
87
+ packet = poll_next_packet
88
+ return packet if packet
89
+
87
90
  if available_for_read?
88
91
  if fill <= 0
89
- raise Net::SSH::Disconnect, "connection closed by remote host"
92
+ result = poll_next_packet
93
+ if result.nil?
94
+ raise Net::SSH::Disconnect, "connection closed by remote host"
95
+ else
96
+ return result
97
+ end
90
98
  end
91
99
  end
92
100
  poll_next_packet