net-ssh 2.9.2 → 4.0.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.gitignore +6 -0
- data/.rubocop.yml +5 -0
- data/.rubocop_todo.yml +1129 -0
- data/.travis.yml +41 -5
- data/CHANGES.txt +133 -1
- data/Gemfile +13 -0
- data/Gemfile.norbnacl +10 -0
- data/Gemfile.norbnacl.lock +41 -0
- data/ISSUE_TEMPLATE.md +30 -0
- data/README.rdoc +26 -81
- data/Rakefile +63 -45
- data/appveyor.yml +51 -0
- data/lib/net/ssh/authentication/agent.rb +174 -14
- data/lib/net/ssh/authentication/ed25519.rb +137 -0
- data/lib/net/ssh/authentication/ed25519_loader.rb +21 -0
- data/lib/net/ssh/authentication/key_manager.rb +36 -30
- data/lib/net/ssh/authentication/methods/abstract.rb +4 -0
- data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +16 -9
- data/lib/net/ssh/authentication/methods/password.rb +17 -4
- data/lib/net/ssh/authentication/pageant.rb +166 -45
- data/lib/net/ssh/authentication/session.rb +3 -2
- data/lib/net/ssh/buffer.rb +49 -10
- data/lib/net/ssh/buffered_io.rb +17 -12
- data/lib/net/ssh/config.rb +39 -8
- data/lib/net/ssh/connection/channel.rb +42 -20
- data/lib/net/ssh/connection/event_loop.rb +114 -0
- data/lib/net/ssh/connection/keepalive.rb +2 -2
- data/lib/net/ssh/connection/session.rb +120 -34
- data/lib/net/ssh/errors.rb +6 -6
- data/lib/net/ssh/key_factory.rb +49 -43
- data/lib/net/ssh/known_hosts.rb +49 -3
- data/lib/net/ssh/prompt.rb +47 -78
- data/lib/net/ssh/proxy/command.rb +31 -5
- data/lib/net/ssh/proxy/http.rb +15 -11
- data/lib/net/ssh/proxy/https.rb +49 -0
- data/lib/net/ssh/proxy/socks4.rb +2 -1
- data/lib/net/ssh/proxy/socks5.rb +3 -2
- data/lib/net/ssh/ruby_compat.rb +2 -29
- data/lib/net/ssh/service/forward.rb +2 -2
- data/lib/net/ssh/test/channel.rb +7 -0
- data/lib/net/ssh/test/extensions.rb +17 -0
- data/lib/net/ssh/test/kex.rb +4 -4
- data/lib/net/ssh/test/packet.rb +18 -2
- data/lib/net/ssh/test/script.rb +16 -2
- data/lib/net/ssh/test/socket.rb +1 -1
- data/lib/net/ssh/test.rb +5 -5
- data/lib/net/ssh/transport/algorithms.rb +92 -75
- data/lib/net/ssh/transport/cipher_factory.rb +19 -26
- data/lib/net/ssh/transport/ctr.rb +7 -9
- data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +20 -9
- data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +5 -3
- data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +1 -1
- data/lib/net/ssh/transport/key_expander.rb +1 -0
- data/lib/net/ssh/transport/openssl.rb +1 -1
- data/lib/net/ssh/transport/packet_stream.rb +11 -3
- data/lib/net/ssh/transport/server_version.rb +13 -6
- data/lib/net/ssh/transport/session.rb +20 -10
- data/lib/net/ssh/transport/state.rb +1 -1
- data/lib/net/ssh/verifiers/secure.rb +8 -10
- data/lib/net/ssh/version.rb +4 -4
- data/lib/net/ssh.rb +62 -14
- data/net-ssh-public_cert.pem +19 -18
- data/net-ssh.gemspec +34 -194
- data/support/arcfour_check.rb +1 -1
- data/support/ssh_tunnel_bug.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +125 -109
- metadata.gz.sig +0 -0
- data/Rudyfile +0 -96
- data/lib/net/ssh/authentication/agent/java_pageant.rb +0 -85
- data/lib/net/ssh/authentication/agent/socket.rb +0 -178
- data/setup.rb +0 -1585
- data/test/README.txt +0 -47
- data/test/authentication/methods/common.rb +0 -28
- data/test/authentication/methods/test_abstract.rb +0 -51
- data/test/authentication/methods/test_hostbased.rb +0 -114
- data/test/authentication/methods/test_keyboard_interactive.rb +0 -100
- data/test/authentication/methods/test_none.rb +0 -41
- data/test/authentication/methods/test_password.rb +0 -95
- data/test/authentication/methods/test_publickey.rb +0 -148
- data/test/authentication/test_agent.rb +0 -224
- data/test/authentication/test_key_manager.rb +0 -227
- data/test/authentication/test_session.rb +0 -107
- data/test/common.rb +0 -108
- data/test/configs/auth_off +0 -5
- data/test/configs/auth_on +0 -4
- data/test/configs/empty +0 -0
- data/test/configs/eqsign +0 -3
- data/test/configs/exact_match +0 -8
- data/test/configs/host_plus +0 -10
- data/test/configs/multihost +0 -4
- data/test/configs/negative_match +0 -6
- data/test/configs/nohost +0 -19
- data/test/configs/numeric_host +0 -4
- data/test/configs/send_env +0 -2
- data/test/configs/substitutes +0 -8
- data/test/configs/wild_cards +0 -14
- data/test/connection/test_channel.rb +0 -467
- data/test/connection/test_session.rb +0 -543
- data/test/known_hosts/github +0 -1
- data/test/manual/test_forward.rb +0 -285
- data/test/manual/test_pageant.rb +0 -37
- data/test/start/test_connection.rb +0 -53
- data/test/start/test_options.rb +0 -43
- data/test/start/test_transport.rb +0 -28
- data/test/test_all.rb +0 -11
- data/test/test_buffer.rb +0 -433
- data/test/test_buffered_io.rb +0 -63
- data/test/test_config.rb +0 -221
- data/test/test_key_factory.rb +0 -191
- data/test/test_known_hosts.rb +0 -13
- data/test/transport/hmac/test_md5.rb +0 -41
- data/test/transport/hmac/test_md5_96.rb +0 -27
- data/test/transport/hmac/test_none.rb +0 -34
- data/test/transport/hmac/test_ripemd160.rb +0 -36
- data/test/transport/hmac/test_sha1.rb +0 -36
- data/test/transport/hmac/test_sha1_96.rb +0 -27
- data/test/transport/hmac/test_sha2_256.rb +0 -37
- data/test/transport/hmac/test_sha2_256_96.rb +0 -27
- data/test/transport/hmac/test_sha2_512.rb +0 -37
- data/test/transport/hmac/test_sha2_512_96.rb +0 -27
- data/test/transport/kex/test_diffie_hellman_group14_sha1.rb +0 -13
- data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +0 -146
- data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +0 -92
- data/test/transport/kex/test_diffie_hellman_group_exchange_sha256.rb +0 -34
- data/test/transport/kex/test_ecdh_sha2_nistp256.rb +0 -161
- data/test/transport/kex/test_ecdh_sha2_nistp384.rb +0 -38
- data/test/transport/kex/test_ecdh_sha2_nistp521.rb +0 -38
- data/test/transport/test_algorithms.rb +0 -324
- data/test/transport/test_cipher_factory.rb +0 -443
- data/test/transport/test_hmac.rb +0 -34
- data/test/transport/test_identity_cipher.rb +0 -40
- data/test/transport/test_packet_stream.rb +0 -1761
- data/test/transport/test_server_version.rb +0 -78
- data/test/transport/test_session.rb +0 -331
- data/test/transport/test_state.rb +0 -181
data/lib/net/ssh/test/kex.rb
CHANGED
|
@@ -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
|
-
{ :
|
|
35
|
-
:
|
|
36
|
-
:
|
|
37
|
-
:
|
|
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
|
|
data/lib/net/ssh/test/packet.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
data/lib/net/ssh/test/script.rb
CHANGED
|
@@ -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
|
-
|
|
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.
|
data/lib/net/ssh/test/socket.rb
CHANGED
|
@@ -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 '
|
|
13
|
+
# require 'minitest/autorun'
|
|
14
14
|
# require 'net/ssh/test'
|
|
15
15
|
#
|
|
16
|
-
# class MyTest < Test
|
|
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(:
|
|
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
|
-
:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
:
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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,
|
|
214
|
-
algorithms[algorithm] =
|
|
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 =
|
|
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 = { :
|
|
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
|
-
:
|
|
294
|
-
:
|
|
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
|
-
:
|
|
359
|
-
:
|
|
360
|
-
:
|
|
361
|
-
:
|
|
362
|
-
:
|
|
363
|
-
:
|
|
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 = { :
|
|
382
|
-
|
|
383
|
-
cipher_client = CipherFactory.get(encryption_client, parameters.merge(:
|
|
384
|
-
cipher_server = CipherFactory.get(encryption_server, parameters.merge(:
|
|
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 :
|
|
390
|
-
:
|
|
391
|
-
:
|
|
392
|
-
:
|
|
393
|
-
:
|
|
394
|
-
:
|
|
395
|
-
|
|
396
|
-
session.configure_server :
|
|
397
|
-
:
|
|
398
|
-
:
|
|
399
|
-
:
|
|
400
|
-
:
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
18
|
-
|
|
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
|
-
|
|
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 { :
|
|
73
|
-
:
|
|
74
|
-
:
|
|
75
|
-
:
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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(:
|
|
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
|
-
|
|
27
|
-
|
|
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,
|
|
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, :
|
|
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
|
|
@@ -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
|
-
:
|
|
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
|
-
|
|
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
|