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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.dockerignore +6 -0
- data/.github/FUNDING.yml +1 -0
- data/.github/config/rubocop_linter_action.yml +4 -0
- data/.github/workflows/ci-with-docker.yml +44 -0
- data/.github/workflows/ci.yml +94 -0
- data/.github/workflows/rubocop.yml +16 -0
- data/.gitignore +4 -0
- data/.rubocop.yml +12 -1
- data/.rubocop_todo.yml +475 -376
- data/CHANGES.txt +64 -3
- data/DEVELOPMENT.md +23 -0
- data/Dockerfile +29 -0
- data/Dockerfile.openssl3 +17 -0
- data/Gemfile +2 -0
- data/Gemfile.noed25519 +2 -0
- data/Gemfile.norbnacl +12 -0
- data/README.md +38 -22
- data/Rakefile +92 -0
- data/SECURITY.md +4 -0
- data/docker-compose.yml +25 -0
- data/lib/net/ssh/authentication/agent.rb +29 -13
- data/lib/net/ssh/authentication/certificate.rb +14 -11
- data/lib/net/ssh/authentication/constants.rb +0 -1
- data/lib/net/ssh/authentication/ed25519.rb +14 -11
- data/lib/net/ssh/authentication/ed25519_loader.rb +4 -7
- data/lib/net/ssh/authentication/key_manager.rb +65 -36
- data/lib/net/ssh/authentication/methods/abstract.rb +12 -3
- data/lib/net/ssh/authentication/methods/hostbased.rb +3 -5
- data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +2 -2
- data/lib/net/ssh/authentication/methods/none.rb +6 -9
- data/lib/net/ssh/authentication/methods/password.rb +2 -3
- data/lib/net/ssh/authentication/methods/publickey.rb +57 -17
- data/lib/net/ssh/authentication/pageant.rb +97 -97
- data/lib/net/ssh/authentication/pub_key_fingerprint.rb +3 -3
- data/lib/net/ssh/authentication/session.rb +25 -17
- data/lib/net/ssh/buffer.rb +71 -51
- data/lib/net/ssh/buffered_io.rb +25 -26
- data/lib/net/ssh/config.rb +33 -20
- data/lib/net/ssh/connection/channel.rb +84 -82
- data/lib/net/ssh/connection/constants.rb +0 -4
- data/lib/net/ssh/connection/event_loop.rb +30 -24
- data/lib/net/ssh/connection/keepalive.rb +12 -12
- data/lib/net/ssh/connection/session.rb +109 -108
- data/lib/net/ssh/connection/term.rb +56 -58
- data/lib/net/ssh/errors.rb +12 -12
- data/lib/net/ssh/key_factory.rb +7 -8
- data/lib/net/ssh/known_hosts.rb +86 -18
- data/lib/net/ssh/loggable.rb +8 -9
- data/lib/net/ssh/packet.rb +1 -1
- data/lib/net/ssh/prompt.rb +9 -11
- data/lib/net/ssh/proxy/command.rb +1 -1
- data/lib/net/ssh/proxy/errors.rb +2 -4
- data/lib/net/ssh/proxy/http.rb +18 -20
- data/lib/net/ssh/proxy/https.rb +8 -10
- data/lib/net/ssh/proxy/jump.rb +8 -10
- data/lib/net/ssh/proxy/socks4.rb +2 -4
- data/lib/net/ssh/proxy/socks5.rb +3 -5
- data/lib/net/ssh/service/forward.rb +7 -7
- data/lib/net/ssh/test/channel.rb +24 -26
- data/lib/net/ssh/test/extensions.rb +35 -35
- data/lib/net/ssh/test/kex.rb +6 -8
- data/lib/net/ssh/test/local_packet.rb +0 -2
- data/lib/net/ssh/test/packet.rb +3 -3
- data/lib/net/ssh/test/remote_packet.rb +6 -8
- data/lib/net/ssh/test/script.rb +25 -27
- data/lib/net/ssh/test/socket.rb +12 -15
- data/lib/net/ssh/test.rb +4 -5
- data/lib/net/ssh/transport/aes128_gcm.rb +40 -0
- data/lib/net/ssh/transport/aes256_gcm.rb +40 -0
- data/lib/net/ssh/transport/algorithms.rb +51 -19
- data/lib/net/ssh/transport/chacha20_poly1305_cipher.rb +117 -0
- data/lib/net/ssh/transport/chacha20_poly1305_cipher_loader.rb +17 -0
- data/lib/net/ssh/transport/cipher_factory.rb +56 -29
- data/lib/net/ssh/transport/constants.rb +3 -3
- data/lib/net/ssh/transport/ctr.rb +7 -7
- data/lib/net/ssh/transport/gcm_cipher.rb +207 -0
- data/lib/net/ssh/transport/hmac/abstract.rb +20 -5
- data/lib/net/ssh/transport/hmac/md5.rb +0 -2
- data/lib/net/ssh/transport/hmac/md5_96.rb +0 -2
- data/lib/net/ssh/transport/hmac/none.rb +0 -2
- data/lib/net/ssh/transport/hmac/ripemd160.rb +0 -2
- data/lib/net/ssh/transport/hmac/sha1.rb +0 -2
- data/lib/net/ssh/transport/hmac/sha1_96.rb +0 -2
- data/lib/net/ssh/transport/hmac.rb +12 -12
- data/lib/net/ssh/transport/identity_cipher.rb +19 -13
- data/lib/net/ssh/transport/kex/abstract.rb +12 -5
- data/lib/net/ssh/transport/kex/abstract5656.rb +1 -1
- data/lib/net/ssh/transport/kex/curve25519_sha256.rb +2 -1
- data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +4 -4
- data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
- data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +21 -21
- data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +1 -2
- data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +2 -2
- data/lib/net/ssh/transport/kex.rb +8 -6
- data/lib/net/ssh/transport/key_expander.rb +7 -8
- data/lib/net/ssh/transport/openssl.rb +51 -26
- data/lib/net/ssh/transport/openssl_cipher_extensions.rb +8 -0
- data/lib/net/ssh/transport/packet_stream.rb +46 -26
- data/lib/net/ssh/transport/server_version.rb +17 -16
- data/lib/net/ssh/transport/session.rb +9 -7
- data/lib/net/ssh/transport/state.rb +44 -44
- data/lib/net/ssh/verifiers/accept_new.rb +0 -2
- data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +1 -2
- data/lib/net/ssh/verifiers/always.rb +6 -4
- data/lib/net/ssh/verifiers/never.rb +0 -2
- data/lib/net/ssh/version.rb +2 -2
- data/lib/net/ssh.rb +15 -8
- data/net-ssh-public_cert.pem +19 -18
- data/net-ssh.gemspec +7 -4
- data/support/ssh_tunnel_bug.rb +3 -3
- data.tar.gz.sig +0 -0
- metadata +76 -29
- metadata.gz.sig +0 -0
- data/.travis.yml +0 -52
|
@@ -6,16 +6,14 @@ require 'net/ssh/connection/constants'
|
|
|
6
6
|
require 'net/ssh/transport/constants'
|
|
7
7
|
require 'net/ssh/transport/packet_stream'
|
|
8
8
|
|
|
9
|
-
module Net
|
|
10
|
-
module SSH
|
|
9
|
+
module Net
|
|
10
|
+
module SSH
|
|
11
11
|
module Test
|
|
12
|
-
|
|
13
12
|
# A collection of modules used to extend/override the default behavior of
|
|
14
13
|
# Net::SSH internals for ease of testing. As a consumer of Net::SSH, you'll
|
|
15
14
|
# never need to use this directly--they're all used under the covers by
|
|
16
15
|
# the Net::SSH::Test system.
|
|
17
16
|
module Extensions
|
|
18
|
-
|
|
19
17
|
# An extension to Net::SSH::BufferedIo (assumes that the underlying IO
|
|
20
18
|
# is actually a StringIO). Facilitates unit testing.
|
|
21
19
|
module BufferedIo
|
|
@@ -24,80 +22,82 @@ module Net
|
|
|
24
22
|
def select_for_read?
|
|
25
23
|
pos < size
|
|
26
24
|
end
|
|
27
|
-
|
|
25
|
+
|
|
28
26
|
# Set this to +true+ if you want the IO to pretend to be available for writing
|
|
29
27
|
attr_accessor :select_for_write
|
|
30
|
-
|
|
28
|
+
|
|
31
29
|
# Set this to +true+ if you want the IO to pretend to be in an error state
|
|
32
30
|
attr_accessor :select_for_error
|
|
33
|
-
|
|
31
|
+
|
|
34
32
|
alias select_for_write? select_for_write
|
|
35
33
|
alias select_for_error? select_for_error
|
|
36
34
|
end
|
|
37
|
-
|
|
35
|
+
|
|
38
36
|
# An extension to Net::SSH::Transport::PacketStream (assumes that the
|
|
39
37
|
# underlying IO is actually a StringIO). Facilitates unit testing.
|
|
40
38
|
module PacketStream
|
|
41
39
|
include BufferedIo # make sure we get the extensions here, too
|
|
42
|
-
|
|
43
|
-
def self.included(base)
|
|
40
|
+
|
|
41
|
+
def self.included(base) # :nodoc:
|
|
44
42
|
base.send :alias_method, :real_available_for_read?, :available_for_read?
|
|
45
43
|
base.send :alias_method, :available_for_read?, :test_available_for_read?
|
|
46
|
-
|
|
44
|
+
|
|
47
45
|
base.send :alias_method, :real_enqueue_packet, :enqueue_packet
|
|
48
46
|
base.send :alias_method, :enqueue_packet, :test_enqueue_packet
|
|
49
|
-
|
|
47
|
+
|
|
50
48
|
base.send :alias_method, :real_poll_next_packet, :poll_next_packet
|
|
51
49
|
base.send :alias_method, :poll_next_packet, :test_poll_next_packet
|
|
52
50
|
end
|
|
53
|
-
|
|
51
|
+
|
|
54
52
|
# Called when another packet should be inspected from the current
|
|
55
53
|
# script. If the next packet is a remote packet, it pops it off the
|
|
56
54
|
# script and shoves it onto this IO object, making it available to
|
|
57
55
|
# be read.
|
|
58
56
|
def idle!
|
|
59
57
|
return false unless script.next(:first)
|
|
60
|
-
|
|
58
|
+
|
|
61
59
|
if script.next(:first).remote?
|
|
62
60
|
self.string << script.next.to_s
|
|
63
61
|
self.pos = pos
|
|
64
62
|
end
|
|
65
|
-
|
|
63
|
+
|
|
66
64
|
return true
|
|
67
65
|
end
|
|
68
|
-
|
|
66
|
+
|
|
69
67
|
# The testing version of Net::SSH::Transport::PacketStream#available_for_read?.
|
|
70
68
|
# Returns true if there is data pending to be read. Otherwise calls #idle!.
|
|
71
69
|
def test_available_for_read?
|
|
72
70
|
return true if select_for_read?
|
|
71
|
+
|
|
73
72
|
idle!
|
|
74
73
|
false
|
|
75
74
|
end
|
|
76
|
-
|
|
75
|
+
|
|
77
76
|
# The testing version of Net::SSH::Transport::PacketStream#enqueued_packet.
|
|
78
77
|
# Simply calls Net::SSH::Test::Script#process on the packet.
|
|
79
78
|
def test_enqueue_packet(payload)
|
|
80
79
|
packet = Net::SSH::Buffer.new(payload.to_s)
|
|
81
80
|
script.process(packet)
|
|
82
81
|
end
|
|
83
|
-
|
|
82
|
+
|
|
84
83
|
# The testing version of Net::SSH::Transport::PacketStream#poll_next_packet.
|
|
85
84
|
# Reads the next available packet from the IO object and returns it.
|
|
86
85
|
def test_poll_next_packet
|
|
87
86
|
return nil if available <= 0
|
|
87
|
+
|
|
88
88
|
packet = Net::SSH::Buffer.new(read_available(4))
|
|
89
89
|
length = packet.read_long
|
|
90
90
|
Net::SSH::Packet.new(read_available(length))
|
|
91
91
|
end
|
|
92
92
|
end
|
|
93
|
-
|
|
93
|
+
|
|
94
94
|
# An extension to Net::SSH::Connection::Channel. Facilitates unit testing.
|
|
95
95
|
module Channel
|
|
96
|
-
def self.included(base)
|
|
96
|
+
def self.included(base) # :nodoc:
|
|
97
97
|
base.send :alias_method, :send_data_for_real, :send_data
|
|
98
98
|
base.send :alias_method, :send_data, :send_data_for_test
|
|
99
99
|
end
|
|
100
|
-
|
|
100
|
+
|
|
101
101
|
# The testing version of Net::SSH::Connection::Channel#send_data. Calls
|
|
102
102
|
# the original implementation, and then immediately enqueues the data for
|
|
103
103
|
# output so that scripted sends are properly interpreted as discrete
|
|
@@ -107,16 +107,16 @@ module Net
|
|
|
107
107
|
enqueue_pending_output
|
|
108
108
|
end
|
|
109
109
|
end
|
|
110
|
-
|
|
110
|
+
|
|
111
111
|
# An extension to the built-in ::IO class. Simply redefines IO.select
|
|
112
112
|
# so that it can be scripted in Net::SSH unit tests.
|
|
113
113
|
module IO
|
|
114
|
-
def self.included(base)
|
|
114
|
+
def self.included(base) # :nodoc:
|
|
115
115
|
base.extend(ClassMethods)
|
|
116
116
|
end
|
|
117
|
-
|
|
117
|
+
|
|
118
118
|
@extension_enabled = false
|
|
119
|
-
|
|
119
|
+
|
|
120
120
|
def self.with_test_extension(&block)
|
|
121
121
|
orig_value = @extension_enabled
|
|
122
122
|
@extension_enabled = true
|
|
@@ -126,35 +126,36 @@ module Net
|
|
|
126
126
|
@extension_enabled = orig_value
|
|
127
127
|
end
|
|
128
128
|
end
|
|
129
|
-
|
|
129
|
+
|
|
130
130
|
def self.extension_enabled?
|
|
131
131
|
@extension_enabled
|
|
132
132
|
end
|
|
133
|
-
|
|
133
|
+
|
|
134
134
|
module ClassMethods
|
|
135
|
-
def self.extended(obj)
|
|
136
|
-
class <<obj
|
|
135
|
+
def self.extended(obj) # :nodoc:
|
|
136
|
+
class << obj
|
|
137
137
|
alias_method :select_for_real, :select
|
|
138
138
|
alias_method :select, :select_for_test
|
|
139
139
|
end
|
|
140
140
|
end
|
|
141
|
-
|
|
141
|
+
|
|
142
142
|
# The testing version of ::IO.select. Assumes that all readers,
|
|
143
143
|
# writers, and errors arrays are either nil, or contain only objects
|
|
144
144
|
# that mix in Net::SSH::Test::Extensions::BufferedIo.
|
|
145
|
-
def select_for_test(readers=nil, writers=nil, errors=nil, wait=nil)
|
|
145
|
+
def select_for_test(readers = nil, writers = nil, errors = nil, wait = nil)
|
|
146
146
|
return select_for_real(readers, writers, errors, wait) unless Net::SSH::Test::Extensions::IO.extension_enabled?
|
|
147
|
+
|
|
147
148
|
ready_readers = Array(readers).select { |r| r.select_for_read? }
|
|
148
149
|
ready_writers = Array(writers).select { |r| r.select_for_write? }
|
|
149
150
|
ready_errors = Array(errors).select { |r| r.select_for_error? }
|
|
150
|
-
|
|
151
|
+
|
|
151
152
|
return [ready_readers, ready_writers, ready_errors] if ready_readers.any? || ready_writers.any? || ready_errors.any?
|
|
152
|
-
|
|
153
|
+
|
|
153
154
|
processed = 0
|
|
154
155
|
Array(readers).each do |reader|
|
|
155
156
|
processed += 1 if reader.idle!
|
|
156
157
|
end
|
|
157
|
-
|
|
158
|
+
|
|
158
159
|
raise "no readers were ready for reading, and none had any incoming packets" if processed == 0 && wait != 0
|
|
159
160
|
|
|
160
161
|
[[], [], []]
|
|
@@ -162,7 +163,6 @@ module Net
|
|
|
162
163
|
end
|
|
163
164
|
end
|
|
164
165
|
end
|
|
165
|
-
|
|
166
166
|
end
|
|
167
167
|
end
|
|
168
168
|
end
|
data/lib/net/ssh/test/kex.rb
CHANGED
|
@@ -5,10 +5,9 @@ require 'net/ssh/transport/algorithms'
|
|
|
5
5
|
require 'net/ssh/transport/constants'
|
|
6
6
|
require 'net/ssh/transport/kex'
|
|
7
7
|
|
|
8
|
-
module Net
|
|
9
|
-
module SSH
|
|
8
|
+
module Net
|
|
9
|
+
module SSH
|
|
10
10
|
module Test
|
|
11
|
-
|
|
12
11
|
# An implementation of a key-exchange strategy specifically for unit tests.
|
|
13
12
|
# (This strategy would never really work against a real SSH server--it makes
|
|
14
13
|
# too many assumptions about the server's response.)
|
|
@@ -17,29 +16,28 @@ module Net
|
|
|
17
16
|
# "test" algorithm.
|
|
18
17
|
class Kex
|
|
19
18
|
include Net::SSH::Transport::Constants
|
|
20
|
-
|
|
19
|
+
|
|
21
20
|
# Creates a new instance of the testing key-exchange algorithm with the
|
|
22
21
|
# given arguments.
|
|
23
22
|
def initialize(algorithms, connection, data)
|
|
24
23
|
@connection = connection
|
|
25
24
|
end
|
|
26
|
-
|
|
25
|
+
|
|
27
26
|
# Exchange keys with the server. This returns a hash of constant values,
|
|
28
27
|
# and does not actually exchange keys.
|
|
29
28
|
def exchange_keys
|
|
30
29
|
result = Net::SSH::Buffer.from(:byte, NEWKEYS)
|
|
31
30
|
@connection.send_message(result)
|
|
32
|
-
|
|
31
|
+
|
|
33
32
|
buffer = @connection.next_message
|
|
34
33
|
raise Net::SSH::Exception, "expected NEWKEYS" unless buffer.type == NEWKEYS
|
|
35
|
-
|
|
34
|
+
|
|
36
35
|
{ session_id: "abc-xyz",
|
|
37
36
|
server_key: OpenSSL::PKey::RSA.new(512),
|
|
38
37
|
shared_secret: OpenSSL::BN.new("1234567890", 10),
|
|
39
38
|
hashing_algorithm: OpenSSL::Digest::SHA1 }
|
|
40
39
|
end
|
|
41
40
|
end
|
|
42
|
-
|
|
43
41
|
end
|
|
44
42
|
end
|
|
45
43
|
end
|
|
@@ -4,7 +4,6 @@ require 'net/ssh/test/packet'
|
|
|
4
4
|
module Net
|
|
5
5
|
module SSH
|
|
6
6
|
module Test
|
|
7
|
-
|
|
8
7
|
# This is a specialization of Net::SSH::Test::Packet for representing mock
|
|
9
8
|
# packets that are sent from the local (client) host. These are created
|
|
10
9
|
# automatically by Net::SSH::Test::Script and Net::SSH::Test::Channel by any
|
|
@@ -49,7 +48,6 @@ module Net
|
|
|
49
48
|
end
|
|
50
49
|
end
|
|
51
50
|
end
|
|
52
|
-
|
|
53
51
|
end
|
|
54
52
|
end
|
|
55
53
|
end
|
data/lib/net/ssh/test/packet.rb
CHANGED
|
@@ -4,7 +4,6 @@ require 'net/ssh/transport/constants'
|
|
|
4
4
|
module Net
|
|
5
5
|
module SSH
|
|
6
6
|
module Test
|
|
7
|
-
|
|
8
7
|
# This is an abstract class, not to be instantiated directly, subclassed by
|
|
9
8
|
# Net::SSH::Test::LocalPacket and Net::SSH::Test::RemotePacket. It implements
|
|
10
9
|
# functionality common to those subclasses.
|
|
@@ -70,7 +69,7 @@ module Net
|
|
|
70
69
|
# added. Unsupported packet types will otherwise raise an exception.
|
|
71
70
|
def types
|
|
72
71
|
@types ||= case @type
|
|
73
|
-
when KEXINIT
|
|
72
|
+
when KEXINIT
|
|
74
73
|
%i[long long long long
|
|
75
74
|
string string string string string string string string string string
|
|
76
75
|
bool]
|
|
@@ -83,13 +82,14 @@ module Net
|
|
|
83
82
|
when CHANNEL_REQUEST
|
|
84
83
|
parts = %i[long string bool]
|
|
85
84
|
case @data[1]
|
|
86
|
-
when "exec", "subsystem","shell" then parts << :string
|
|
85
|
+
when "exec", "subsystem", "shell" then parts << :string
|
|
87
86
|
when "exit-status" then parts << :long
|
|
88
87
|
when "pty-req" then parts.concat(%i[string long long long long string])
|
|
89
88
|
when "env" then parts.contact(%i[string string])
|
|
90
89
|
else
|
|
91
90
|
request = Packet.registered_channel_requests(@data[1])
|
|
92
91
|
raise "don't know what to do about #{@data[1]} channel request" unless request
|
|
92
|
+
|
|
93
93
|
parts.concat(request[:extra_parts])
|
|
94
94
|
end
|
|
95
95
|
else raise "don't know how to parse packet type #{@type}"
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
require 'net/ssh/buffer'
|
|
2
2
|
require 'net/ssh/test/packet'
|
|
3
3
|
|
|
4
|
-
module Net
|
|
5
|
-
module SSH
|
|
4
|
+
module Net
|
|
5
|
+
module SSH
|
|
6
6
|
module Test
|
|
7
|
-
|
|
8
7
|
# This is a specialization of Net::SSH::Test::Packet for representing mock
|
|
9
8
|
# packets that are received by the local (client) host. These are created
|
|
10
9
|
# automatically by Net::SSH::Test::Script and Net::SSH::Test::Channel by any
|
|
@@ -14,7 +13,7 @@ module Net
|
|
|
14
13
|
def remote?
|
|
15
14
|
true
|
|
16
15
|
end
|
|
17
|
-
|
|
16
|
+
|
|
18
17
|
# The #process method should only be called on Net::SSH::Test::LocalPacket
|
|
19
18
|
# packets; if it is attempted on a remote packet, then it is an expectation
|
|
20
19
|
# mismatch (a remote packet was received when a local packet was expected
|
|
@@ -23,8 +22,8 @@ module Net
|
|
|
23
22
|
def process(packet)
|
|
24
23
|
raise "received packet type #{packet.read_byte} and was not expecting any packet"
|
|
25
24
|
end
|
|
26
|
-
|
|
27
|
-
# Returns this remote packet as a string, suitable for parsing by
|
|
25
|
+
|
|
26
|
+
# Returns this remote packet as a string, suitable for parsing by
|
|
28
27
|
# Net::SSH::Transport::PacketStream and friends. When a remote packet is
|
|
29
28
|
# received, this method is called and the result concatenated onto the
|
|
30
29
|
# input buffer for the packet stream.
|
|
@@ -36,7 +35,6 @@ module Net
|
|
|
36
35
|
end
|
|
37
36
|
end
|
|
38
37
|
end
|
|
39
|
-
|
|
40
38
|
end
|
|
41
39
|
end
|
|
42
|
-
end
|
|
40
|
+
end
|
data/lib/net/ssh/test/script.rb
CHANGED
|
@@ -2,10 +2,9 @@ require 'net/ssh/test/channel'
|
|
|
2
2
|
require 'net/ssh/test/local_packet'
|
|
3
3
|
require 'net/ssh/test/remote_packet'
|
|
4
4
|
|
|
5
|
-
module Net
|
|
6
|
-
module SSH
|
|
5
|
+
module Net
|
|
6
|
+
module SSH
|
|
7
7
|
module Test
|
|
8
|
-
|
|
9
8
|
# Represents a sequence of scripted events that identify the behavior that
|
|
10
9
|
# a test expects. Methods named "sends_*" create events for packets being
|
|
11
10
|
# sent from the local to the remote host, and methods named "gets_*" create
|
|
@@ -22,41 +21,41 @@ module Net
|
|
|
22
21
|
# The list of scripted events. These will be Net::SSH::Test::LocalPacket
|
|
23
22
|
# and Net::SSH::Test::RemotePacket instances.
|
|
24
23
|
attr_reader :events
|
|
25
|
-
|
|
24
|
+
|
|
26
25
|
# Create a new, empty script.
|
|
27
26
|
def initialize
|
|
28
27
|
@events = []
|
|
29
28
|
end
|
|
30
|
-
|
|
29
|
+
|
|
31
30
|
# Scripts the opening of a channel by adding a local packet sending the
|
|
32
31
|
# channel open request, and if +confirm+ is true (the default), also
|
|
33
32
|
# adding a remote packet confirming the new channel.
|
|
34
33
|
#
|
|
35
34
|
# A new Net::SSH::Test::Channel instance is returned, which can be used
|
|
36
35
|
# to script additional channel operations.
|
|
37
|
-
def opens_channel(confirm=true)
|
|
36
|
+
def opens_channel(confirm = true)
|
|
38
37
|
channel = Channel.new(self)
|
|
39
38
|
channel.remote_id = 5555
|
|
40
|
-
|
|
39
|
+
|
|
41
40
|
events << LocalPacket.new(:channel_open) { |p| channel.local_id = p[:remote_id] }
|
|
42
|
-
|
|
41
|
+
|
|
43
42
|
events << RemotePacket.new(:channel_open_confirmation, channel.local_id, channel.remote_id, 0x20000, 0x10000) if confirm
|
|
44
|
-
|
|
43
|
+
|
|
45
44
|
channel
|
|
46
45
|
end
|
|
47
|
-
|
|
46
|
+
|
|
48
47
|
# A convenience method for adding an arbitrary local packet to the events
|
|
49
48
|
# list.
|
|
50
49
|
def sends(type, *args, &block)
|
|
51
50
|
events << LocalPacket.new(type, *args, &block)
|
|
52
51
|
end
|
|
53
|
-
|
|
52
|
+
|
|
54
53
|
# A convenience method for adding an arbitrary remote packet to the events
|
|
55
54
|
# list.
|
|
56
55
|
def gets(type, *args)
|
|
57
56
|
events << RemotePacket.new(type, *args)
|
|
58
57
|
end
|
|
59
|
-
|
|
58
|
+
|
|
60
59
|
# Scripts the sending of a new channel request packet to the remote host.
|
|
61
60
|
# +channel+ should be an instance of Net::SSH::Test::Channel. +request+
|
|
62
61
|
# is a string naming the request type to send, +reply+ is a boolean
|
|
@@ -71,7 +70,7 @@ module Net
|
|
|
71
70
|
#
|
|
72
71
|
# This will typically be called via Net::SSH::Test::Channel#sends_exec or
|
|
73
72
|
# Net::SSH::Test::Channel#sends_subsystem.
|
|
74
|
-
def sends_channel_request(channel, request, reply, data, success=true)
|
|
73
|
+
def sends_channel_request(channel, request, reply, data, success = true)
|
|
75
74
|
if data.is_a? Array
|
|
76
75
|
events << LocalPacket.new(:channel_request, channel.remote_id, request, reply, *data)
|
|
77
76
|
else
|
|
@@ -85,7 +84,7 @@ module Net
|
|
|
85
84
|
end
|
|
86
85
|
end
|
|
87
86
|
end
|
|
88
|
-
|
|
87
|
+
|
|
89
88
|
# Scripts the sending of a channel data packet. +channel+ must be a
|
|
90
89
|
# Net::SSH::Test::Channel object, and +data+ is the (string) data to
|
|
91
90
|
# expect will be sent.
|
|
@@ -94,21 +93,21 @@ module Net
|
|
|
94
93
|
def sends_channel_data(channel, data)
|
|
95
94
|
events << LocalPacket.new(:channel_data, channel.remote_id, data)
|
|
96
95
|
end
|
|
97
|
-
|
|
96
|
+
|
|
98
97
|
# Scripts the sending of a channel EOF packet from the given
|
|
99
98
|
# Net::SSH::Test::Channel +channel+. This will typically be called via
|
|
100
99
|
# Net::SSH::Test::Channel#sends_eof.
|
|
101
100
|
def sends_channel_eof(channel)
|
|
102
101
|
events << LocalPacket.new(:channel_eof, channel.remote_id)
|
|
103
102
|
end
|
|
104
|
-
|
|
103
|
+
|
|
105
104
|
# Scripts the sending of a channel close packet from the given
|
|
106
105
|
# Net::SSH::Test::Channel +channel+. This will typically be called via
|
|
107
106
|
# Net::SSH::Test::Channel#sends_close.
|
|
108
107
|
def sends_channel_close(channel)
|
|
109
108
|
events << LocalPacket.new(:channel_close, channel.remote_id)
|
|
110
109
|
end
|
|
111
|
-
|
|
110
|
+
|
|
112
111
|
# Scripts the sending of a channel request pty packets from the given
|
|
113
112
|
# Net::SSH::Test::Channel +channel+. This will typically be called via
|
|
114
113
|
# Net::SSH::Test::Channel#sends_request_pty.
|
|
@@ -117,14 +116,14 @@ module Net
|
|
|
117
116
|
data += Net::SSH::Connection::Channel::VALID_PTY_OPTIONS.merge(modes: "\0").values
|
|
118
117
|
events << LocalPacket.new(:channel_request, channel.remote_id, *data)
|
|
119
118
|
end
|
|
120
|
-
|
|
119
|
+
|
|
121
120
|
# Scripts the reception of a channel data packet from the remote host by
|
|
122
121
|
# the given Net::SSH::Test::Channel +channel+. This will typically be
|
|
123
122
|
# called via Net::SSH::Test::Channel#gets_data.
|
|
124
123
|
def gets_channel_data(channel, data)
|
|
125
124
|
events << RemotePacket.new(:channel_data, channel.local_id, data)
|
|
126
125
|
end
|
|
127
|
-
|
|
126
|
+
|
|
128
127
|
# Scripts the reception of a channel extended data packet from the remote
|
|
129
128
|
# host by the given Net::SSH::Test::Channel +channel+. This will typically
|
|
130
129
|
# be called via Net::SSH::Test::Channel#gets_extended_data.
|
|
@@ -133,28 +132,28 @@ module Net
|
|
|
133
132
|
def gets_channel_extended_data(channel, data)
|
|
134
133
|
events << RemotePacket.new(:channel_extended_data, channel.local_id, 1, data)
|
|
135
134
|
end
|
|
136
|
-
|
|
135
|
+
|
|
137
136
|
# Scripts the reception of a channel request packet from the remote host by
|
|
138
137
|
# the given Net::SSH::Test::Channel +channel+. This will typically be
|
|
139
138
|
# called via Net::SSH::Test::Channel#gets_exit_status.
|
|
140
139
|
def gets_channel_request(channel, request, reply, data)
|
|
141
140
|
events << RemotePacket.new(:channel_request, channel.local_id, request, reply, data)
|
|
142
141
|
end
|
|
143
|
-
|
|
142
|
+
|
|
144
143
|
# Scripts the reception of a channel EOF packet from the remote host by
|
|
145
144
|
# the given Net::SSH::Test::Channel +channel+. This will typically be
|
|
146
145
|
# called via Net::SSH::Test::Channel#gets_eof.
|
|
147
146
|
def gets_channel_eof(channel)
|
|
148
147
|
events << RemotePacket.new(:channel_eof, channel.local_id)
|
|
149
148
|
end
|
|
150
|
-
|
|
149
|
+
|
|
151
150
|
# Scripts the reception of a channel close packet from the remote host by
|
|
152
151
|
# the given Net::SSH::Test::Channel +channel+. This will typically be
|
|
153
152
|
# called via Net::SSH::Test::Channel#gets_close.
|
|
154
153
|
def gets_channel_close(channel)
|
|
155
154
|
events << RemotePacket.new(:channel_close, channel.local_id)
|
|
156
155
|
end
|
|
157
|
-
|
|
156
|
+
|
|
158
157
|
# By default, removes the next event in the list and returns it. However,
|
|
159
158
|
# this can also be used to non-destructively peek at the next event in the
|
|
160
159
|
# list, by passing :first as the argument.
|
|
@@ -164,10 +163,10 @@ module Net
|
|
|
164
163
|
#
|
|
165
164
|
# # peek at the next event
|
|
166
165
|
# event = script.next(:first)
|
|
167
|
-
def next(mode
|
|
166
|
+
def next(mode = :shift)
|
|
168
167
|
events.send(mode)
|
|
169
168
|
end
|
|
170
|
-
|
|
169
|
+
|
|
171
170
|
# Compare the given packet against the next event in the list. If there is
|
|
172
171
|
# no next event, an exception will be raised. This is called by
|
|
173
172
|
# Net::SSH::Test::Extensions::PacketStream#test_enqueue_packet.
|
|
@@ -176,7 +175,6 @@ module Net
|
|
|
176
175
|
event.process(packet)
|
|
177
176
|
end
|
|
178
177
|
end
|
|
179
|
-
|
|
180
178
|
end
|
|
181
179
|
end
|
|
182
|
-
end
|
|
180
|
+
end
|
data/lib/net/ssh/test/socket.rb
CHANGED
|
@@ -3,66 +3,63 @@ require 'stringio'
|
|
|
3
3
|
require 'net/ssh/test/extensions'
|
|
4
4
|
require 'net/ssh/test/script'
|
|
5
5
|
|
|
6
|
-
module Net
|
|
7
|
-
module SSH
|
|
6
|
+
module Net
|
|
7
|
+
module SSH
|
|
8
8
|
module Test
|
|
9
|
-
|
|
10
9
|
# A mock socket implementation for use in testing. It implements the minimum
|
|
11
10
|
# necessary interface for interacting with the rest of the Net::SSH::Test
|
|
12
11
|
# system.
|
|
13
12
|
class Socket < StringIO
|
|
14
13
|
attr_reader :host, :port
|
|
15
|
-
|
|
14
|
+
|
|
16
15
|
# The Net::SSH::Test::Script object in use by this socket. This is the
|
|
17
16
|
# canonical script instance that should be used for any test depending on
|
|
18
17
|
# this socket instance.
|
|
19
18
|
attr_reader :script
|
|
20
|
-
|
|
19
|
+
|
|
21
20
|
# Create a new test socket. This will also instantiate a new Net::SSH::Test::Script
|
|
22
21
|
# and seed it with the necessary events to power the initialization of the
|
|
23
22
|
# connection.
|
|
24
23
|
def initialize
|
|
25
24
|
extend(Net::SSH::Transport::PacketStream)
|
|
26
25
|
super "SSH-2.0-Test\r\n"
|
|
27
|
-
|
|
26
|
+
|
|
28
27
|
@script = Script.new
|
|
29
|
-
|
|
28
|
+
|
|
30
29
|
script.sends(:kexinit)
|
|
31
30
|
script.gets(:kexinit, 1, 2, 3, 4, "test", "ssh-rsa", "none", "none", "none", "none", "none", "none", "", "", false)
|
|
32
31
|
script.sends(:newkeys)
|
|
33
32
|
script.gets(:newkeys)
|
|
34
33
|
end
|
|
35
|
-
|
|
34
|
+
|
|
36
35
|
# This doesn't actually do anything, since we don't really care what gets
|
|
37
36
|
# written.
|
|
38
37
|
def write(data)
|
|
39
38
|
# black hole, because we don't actually care about what gets written
|
|
40
39
|
end
|
|
41
|
-
|
|
40
|
+
|
|
42
41
|
# Allows the socket to also mimic a socket factory, simply returning
|
|
43
42
|
# +self+.
|
|
44
|
-
def open(host, port, options={})
|
|
43
|
+
def open(host, port, options = {})
|
|
45
44
|
@host, @port = host, port
|
|
46
45
|
self
|
|
47
46
|
end
|
|
48
|
-
|
|
47
|
+
|
|
49
48
|
# Returns a sockaddr struct for the port and host that were used when the
|
|
50
49
|
# socket was instantiated.
|
|
51
50
|
def getpeername
|
|
52
51
|
::Socket.sockaddr_in(port, host)
|
|
53
52
|
end
|
|
54
|
-
|
|
53
|
+
|
|
55
54
|
# Alias to #read, but never returns nil (returns an empty string instead).
|
|
56
55
|
def recv(n)
|
|
57
56
|
read(n) || ""
|
|
58
57
|
end
|
|
59
|
-
|
|
58
|
+
|
|
60
59
|
def readpartial(n)
|
|
61
60
|
recv(n)
|
|
62
61
|
end
|
|
63
|
-
|
|
64
62
|
end
|
|
65
|
-
|
|
66
63
|
end
|
|
67
64
|
end
|
|
68
65
|
end
|
data/lib/net/ssh/test.rb
CHANGED
|
@@ -5,7 +5,6 @@ require 'net/ssh/test/socket'
|
|
|
5
5
|
|
|
6
6
|
module Net
|
|
7
7
|
module SSH
|
|
8
|
-
|
|
9
8
|
# This module may be used in unit tests, for when you want to test that your
|
|
10
9
|
# SSH state machines are really doing what you expect they are doing. You will
|
|
11
10
|
# typically include this module in your unit test class, and then build a
|
|
@@ -57,21 +56,21 @@ module Net
|
|
|
57
56
|
|
|
58
57
|
# Returns the test socket instance to use for these tests (see
|
|
59
58
|
# Net::SSH::Test::Socket).
|
|
60
|
-
def socket(options={})
|
|
59
|
+
def socket(options = {})
|
|
61
60
|
@socket ||= Net::SSH::Test::Socket.new
|
|
62
61
|
end
|
|
63
62
|
|
|
64
63
|
# Returns the connection session (Net::SSH::Connection::Session) for use
|
|
65
64
|
# in these tests. It is a fully functional SSH session, operating over
|
|
66
65
|
# a mock socket (#socket).
|
|
67
|
-
def connection(options={})
|
|
66
|
+
def connection(options = {})
|
|
68
67
|
@connection ||= Net::SSH::Connection::Session.new(transport(options), options)
|
|
69
68
|
end
|
|
70
69
|
|
|
71
70
|
# Returns the transport session (Net::SSH::Transport::Session) for use
|
|
72
71
|
# in these tests. It is a fully functional SSH transport session, operating
|
|
73
72
|
# over a mock socket (#socket).
|
|
74
|
-
def transport(options={})
|
|
73
|
+
def transport(options = {})
|
|
75
74
|
@transport ||= Net::SSH::Transport::Session.new(
|
|
76
75
|
options[:host] || "localhost",
|
|
77
76
|
options.merge(kex: "test", host_key: "ssh-rsa", append_all_supported_algorithms: true, verify_host_key: :never, proxy: socket(options))
|
|
@@ -85,11 +84,11 @@ module Net
|
|
|
85
84
|
# the block passed to this assertion.
|
|
86
85
|
def assert_scripted
|
|
87
86
|
raise "there is no script to be processed" if socket.script.events.empty?
|
|
87
|
+
|
|
88
88
|
Net::SSH::Test::Extensions::IO.with_test_extension { yield }
|
|
89
89
|
assert socket.script.events.empty?, "there should not be any remaining scripted events, but there are still" \
|
|
90
90
|
"#{socket.script.events.length} pending"
|
|
91
91
|
end
|
|
92
92
|
end
|
|
93
|
-
|
|
94
93
|
end
|
|
95
94
|
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require 'net/ssh/transport/hmac/abstract'
|
|
2
|
+
require 'net/ssh/transport/gcm_cipher'
|
|
3
|
+
|
|
4
|
+
module Net::SSH::Transport
|
|
5
|
+
## Implements the aes128-gcm@openssh cipher
|
|
6
|
+
class AES128_GCM
|
|
7
|
+
extend ::Net::SSH::Transport::GCMCipher
|
|
8
|
+
|
|
9
|
+
## Implicit HMAC, do need to do anything
|
|
10
|
+
class ImplicitHMac < ::Net::SSH::Transport::HMAC::Abstract
|
|
11
|
+
def aead
|
|
12
|
+
true
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def key_length
|
|
16
|
+
16
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def implicit_mac
|
|
21
|
+
ImplicitHMac.new
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def algo_name
|
|
25
|
+
'aes-128-gcm'
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def name
|
|
29
|
+
'aes128-gcm@openssh.com'
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
#
|
|
33
|
+
# --- RFC 5647 ---
|
|
34
|
+
# K_LEN AES key length 16 octets
|
|
35
|
+
#
|
|
36
|
+
def self.key_length
|
|
37
|
+
16
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|