net-ssh 5.0.2 → 7.0.1
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/config/rubocop_linter_action.yml +4 -0
- data/.github/workflows/ci-with-docker.yml +44 -0
- data/.github/workflows/ci.yml +87 -0
- data/.github/workflows/rubocop.yml +13 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +19 -2
- data/.rubocop_todo.yml +623 -511
- data/CHANGES.txt +76 -0
- data/Dockerfile +27 -0
- data/Dockerfile.openssl3 +17 -0
- data/Gemfile +2 -0
- data/Gemfile.noed25519 +2 -0
- data/Manifest +0 -1
- data/README.md +293 -0
- data/Rakefile +6 -2
- data/appveyor.yml +4 -2
- data/docker-compose.yml +23 -0
- data/lib/net/ssh/authentication/agent.rb +36 -14
- data/lib/net/ssh/authentication/certificate.rb +19 -7
- data/lib/net/ssh/authentication/constants.rb +0 -1
- data/lib/net/ssh/authentication/ed25519.rb +83 -50
- data/lib/net/ssh/authentication/ed25519_loader.rb +5 -8
- data/lib/net/ssh/authentication/key_manager.rb +74 -33
- 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 +5 -3
- 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 +58 -16
- data/lib/net/ssh/authentication/pageant.rb +97 -97
- data/lib/net/ssh/authentication/pub_key_fingerprint.rb +2 -3
- data/lib/net/ssh/authentication/session.rb +27 -23
- data/lib/net/ssh/buffer.rb +91 -40
- data/lib/net/ssh/buffered_io.rb +24 -26
- data/lib/net/ssh/config.rb +99 -53
- data/lib/net/ssh/connection/channel.rb +101 -87
- data/lib/net/ssh/connection/constants.rb +0 -4
- data/lib/net/ssh/connection/event_loop.rb +30 -25
- data/lib/net/ssh/connection/keepalive.rb +12 -12
- data/lib/net/ssh/connection/session.rb +115 -111
- data/lib/net/ssh/connection/term.rb +56 -58
- data/lib/net/ssh/errors.rb +12 -12
- data/lib/net/ssh/key_factory.rb +108 -22
- data/lib/net/ssh/known_hosts.rb +120 -36
- data/lib/net/ssh/loggable.rb +10 -11
- data/lib/net/ssh/packet.rb +1 -1
- data/lib/net/ssh/prompt.rb +9 -11
- data/lib/net/ssh/proxy/command.rb +1 -2
- 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 -6
- data/lib/net/ssh/service/forward.rb +9 -8
- data/lib/net/ssh/test/channel.rb +24 -26
- data/lib/net/ssh/test/extensions.rb +37 -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 +12 -12
- data/lib/net/ssh/transport/algorithms.rb +177 -118
- data/lib/net/ssh/transport/cipher_factory.rb +34 -50
- data/lib/net/ssh/transport/constants.rb +13 -9
- data/lib/net/ssh/transport/ctr.rb +8 -14
- 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/sha2_256.rb +7 -11
- data/lib/net/ssh/transport/hmac/sha2_256_96.rb +4 -8
- data/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
- data/lib/net/ssh/transport/hmac/sha2_512.rb +6 -9
- data/lib/net/ssh/transport/hmac/sha2_512_96.rb +4 -8
- data/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
- data/lib/net/ssh/transport/hmac.rb +13 -11
- data/lib/net/ssh/transport/identity_cipher.rb +11 -13
- data/lib/net/ssh/transport/kex/abstract.rb +130 -0
- data/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
- data/lib/net/ssh/transport/kex/curve25519_sha256.rb +39 -0
- data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
- data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +5 -19
- data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
- data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +30 -139
- data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +1 -8
- data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +5 -9
- data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +20 -81
- data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +5 -4
- data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +5 -4
- data/lib/net/ssh/transport/kex.rb +15 -10
- data/lib/net/ssh/transport/key_expander.rb +7 -8
- data/lib/net/ssh/transport/openssl.rb +149 -111
- data/lib/net/ssh/transport/packet_stream.rb +53 -22
- data/lib/net/ssh/transport/server_version.rb +17 -16
- data/lib/net/ssh/transport/session.rb +35 -11
- data/lib/net/ssh/transport/state.rb +44 -44
- data/lib/net/ssh/verifiers/accept_new.rb +7 -2
- data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +1 -2
- data/lib/net/ssh/verifiers/always.rb +10 -4
- data/lib/net/ssh/verifiers/never.rb +4 -2
- data/lib/net/ssh/version.rb +2 -2
- data/lib/net/ssh.rb +17 -9
- data/net-ssh-public_cert.pem +18 -19
- data/net-ssh.gemspec +9 -7
- data/support/ssh_tunnel_bug.rb +3 -3
- data.tar.gz.sig +0 -0
- metadata +65 -41
- metadata.gz.sig +0 -0
- data/.travis.yml +0 -52
- data/Gemfile.noed25519.lock +0 -41
- data/README.rdoc +0 -169
- data/lib/net/ssh/ruby_compat.rb +0 -13
- data/support/arcfour_check.rb +0 -20
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
require 'net/ssh/buffered_io'
|
|
2
2
|
require 'net/ssh/errors'
|
|
3
3
|
require 'net/ssh/packet'
|
|
4
|
-
require 'net/ssh/ruby_compat'
|
|
5
4
|
require 'net/ssh/transport/cipher_factory'
|
|
6
5
|
require 'net/ssh/transport/hmac'
|
|
7
6
|
require 'net/ssh/transport/state'
|
|
@@ -9,7 +8,6 @@ require 'net/ssh/transport/state'
|
|
|
9
8
|
module Net
|
|
10
9
|
module SSH
|
|
11
10
|
module Transport
|
|
12
|
-
|
|
13
11
|
# A module that builds additional functionality onto the Net::SSH::BufferedIo
|
|
14
12
|
# module. It adds SSH encryption, compression, and packet validation, as
|
|
15
13
|
# per the SSH2 protocol. It also adds an abstraction for polling packets,
|
|
@@ -81,8 +79,8 @@ module Net
|
|
|
81
79
|
# default), then this will return immediately, whether a packet is
|
|
82
80
|
# available or not, and will return nil if there is no packet ready to be
|
|
83
81
|
# returned. If the mode parameter is :block, then this method will block
|
|
84
|
-
# until a packet is available.
|
|
85
|
-
def next_packet(mode
|
|
82
|
+
# until a packet is available or timeout seconds have passed.
|
|
83
|
+
def next_packet(mode = :nonblock, timeout = nil)
|
|
86
84
|
case mode
|
|
87
85
|
when :nonblock then
|
|
88
86
|
packet = poll_next_packet
|
|
@@ -105,11 +103,8 @@ module Net
|
|
|
105
103
|
packet = poll_next_packet
|
|
106
104
|
return packet if packet
|
|
107
105
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
break if result.first.any?
|
|
111
|
-
end
|
|
112
|
-
|
|
106
|
+
result = IO.select([self], nil, nil, timeout)
|
|
107
|
+
raise Net::SSH::ConnectionTimeout, "timeout waiting for next packet" unless result
|
|
113
108
|
raise Net::SSH::Disconnect, "connection closed by remote host" if fill <= 0
|
|
114
109
|
end
|
|
115
110
|
|
|
@@ -133,7 +128,7 @@ module Net
|
|
|
133
128
|
payload = client.compress(payload)
|
|
134
129
|
|
|
135
130
|
# the length of the packet, minus the padding
|
|
136
|
-
actual_length = 4 + payload.bytesize + 1
|
|
131
|
+
actual_length = (client.hmac.etm ? 0 : 4) + payload.bytesize + 1
|
|
137
132
|
|
|
138
133
|
# compute the padding length
|
|
139
134
|
padding_length = client.block_size - (actual_length % client.block_size)
|
|
@@ -149,11 +144,32 @@ module Net
|
|
|
149
144
|
|
|
150
145
|
padding = Array.new(padding_length) { rand(256) }.pack("C*")
|
|
151
146
|
|
|
152
|
-
|
|
153
|
-
|
|
147
|
+
if client.hmac.etm
|
|
148
|
+
debug { "using encrypt-then-mac" }
|
|
149
|
+
|
|
150
|
+
# Encrypt padding_length, payload, and padding. Take MAC
|
|
151
|
+
# from the unencrypted packet_lenght and the encrypted
|
|
152
|
+
# data.
|
|
153
|
+
length_data = [packet_length].pack("N")
|
|
154
|
+
|
|
155
|
+
unencrypted_data = [padding_length, payload, padding].pack("CA*A*")
|
|
156
|
+
|
|
157
|
+
encrypted_data = client.update_cipher(unencrypted_data) << client.final_cipher
|
|
158
|
+
|
|
159
|
+
mac_data = length_data + encrypted_data
|
|
160
|
+
|
|
161
|
+
mac = client.hmac.digest([client.sequence_number, mac_data].pack("NA*"))
|
|
162
|
+
|
|
163
|
+
message = mac_data + mac
|
|
164
|
+
else
|
|
165
|
+
unencrypted_data = [packet_length, padding_length, payload, padding].pack("NCA*A*")
|
|
166
|
+
|
|
167
|
+
mac = client.hmac.digest([client.sequence_number, unencrypted_data].pack("NA*"))
|
|
154
168
|
|
|
155
|
-
|
|
156
|
-
|
|
169
|
+
encrypted_data = client.update_cipher(unencrypted_data) << client.final_cipher
|
|
170
|
+
|
|
171
|
+
message = encrypted_data + mac
|
|
172
|
+
end
|
|
157
173
|
|
|
158
174
|
debug { "queueing packet nr #{client.sequence_number} type #{payload.getbyte(0)} len #{packet_length}" }
|
|
159
175
|
enqueue(message)
|
|
@@ -198,18 +214,28 @@ module Net
|
|
|
198
214
|
# read, post-processed according to the cipher, hmac, and compression
|
|
199
215
|
# algorithms specified in the server state object, and returned as a
|
|
200
216
|
# new Packet object.
|
|
217
|
+
# rubocop:disable Metrics/AbcSize
|
|
201
218
|
def poll_next_packet
|
|
219
|
+
aad_length = server.hmac.etm ? 4 : 0
|
|
220
|
+
|
|
202
221
|
if @packet.nil?
|
|
203
222
|
minimum = server.block_size < 4 ? 4 : server.block_size
|
|
204
|
-
return nil if available < minimum
|
|
205
|
-
|
|
223
|
+
return nil if available < minimum + aad_length
|
|
224
|
+
|
|
225
|
+
data = read_available(minimum + aad_length)
|
|
206
226
|
|
|
207
227
|
# decipher it
|
|
208
|
-
|
|
209
|
-
|
|
228
|
+
if server.hmac.etm
|
|
229
|
+
@packet_length = data.unpack("N").first
|
|
230
|
+
@mac_data = data
|
|
231
|
+
@packet = Net::SSH::Buffer.new(server.update_cipher(data[aad_length..-1]))
|
|
232
|
+
else
|
|
233
|
+
@packet = Net::SSH::Buffer.new(server.update_cipher(data))
|
|
234
|
+
@packet_length = @packet.read_long
|
|
235
|
+
end
|
|
210
236
|
end
|
|
211
237
|
|
|
212
|
-
need = @packet_length + 4 - server.block_size
|
|
238
|
+
need = @packet_length + 4 - aad_length - server.block_size
|
|
213
239
|
raise Net::SSH::Exception, "padding error, need #{need} block #{server.block_size}" if need % server.block_size != 0
|
|
214
240
|
|
|
215
241
|
return nil if available < need + server.hmac.mac_length
|
|
@@ -217,6 +243,7 @@ module Net
|
|
|
217
243
|
if need > 0
|
|
218
244
|
# read the remainder of the packet and decrypt it.
|
|
219
245
|
data = read_available(need)
|
|
246
|
+
@mac_data += data if server.hmac.etm
|
|
220
247
|
@packet.append(server.update_cipher(data))
|
|
221
248
|
end
|
|
222
249
|
|
|
@@ -229,8 +256,12 @@ module Net
|
|
|
229
256
|
|
|
230
257
|
payload = @packet.read(@packet_length - padding_length - 1)
|
|
231
258
|
|
|
232
|
-
my_computed_hmac = server.hmac.
|
|
233
|
-
|
|
259
|
+
my_computed_hmac = if server.hmac.etm
|
|
260
|
+
server.hmac.digest([server.sequence_number, @mac_data].pack("NA*"))
|
|
261
|
+
else
|
|
262
|
+
server.hmac.digest([server.sequence_number, @packet.content].pack("NA*"))
|
|
263
|
+
end
|
|
264
|
+
raise Net::SSH::Exception, "corrupted hmac detected #{server.hmac.class}" if real_hmac != my_computed_hmac
|
|
234
265
|
|
|
235
266
|
# try to decompress the payload, in case compression is active
|
|
236
267
|
payload = server.decompress(payload)
|
|
@@ -243,7 +274,7 @@ module Net
|
|
|
243
274
|
return Packet.new(payload)
|
|
244
275
|
end
|
|
245
276
|
end
|
|
246
|
-
|
|
277
|
+
# rubocop:enable Metrics/AbcSize
|
|
247
278
|
end
|
|
248
279
|
end
|
|
249
280
|
end
|
|
@@ -2,10 +2,9 @@ require 'net/ssh/errors'
|
|
|
2
2
|
require 'net/ssh/loggable'
|
|
3
3
|
require 'net/ssh/version'
|
|
4
4
|
|
|
5
|
-
module Net
|
|
6
|
-
module SSH
|
|
5
|
+
module Net
|
|
6
|
+
module SSH
|
|
7
7
|
module Transport
|
|
8
|
-
|
|
9
8
|
# Negotiates the SSH protocol version and trades information about server
|
|
10
9
|
# and client. This is never used directly--it is always called by the
|
|
11
10
|
# transport layer as part of the initialization process of the transport
|
|
@@ -15,40 +14,41 @@ module Net
|
|
|
15
14
|
# the authoritative reference for any queries regarding the version in effect.
|
|
16
15
|
class ServerVersion
|
|
17
16
|
include Loggable
|
|
18
|
-
|
|
17
|
+
|
|
19
18
|
# The SSH version string as reported by Net::SSH
|
|
20
19
|
PROTO_VERSION = "SSH-2.0-Ruby/Net::SSH_#{Net::SSH::Version::CURRENT} #{RUBY_PLATFORM}"
|
|
21
|
-
|
|
20
|
+
|
|
22
21
|
# Any header text sent by the server prior to sending the version.
|
|
23
22
|
attr_reader :header
|
|
24
|
-
|
|
23
|
+
|
|
25
24
|
# The version string reported by the server.
|
|
26
25
|
attr_reader :version
|
|
27
|
-
|
|
26
|
+
|
|
28
27
|
# Instantiates a new ServerVersion and immediately (and synchronously)
|
|
29
28
|
# negotiates the SSH protocol in effect, using the given socket.
|
|
30
29
|
def initialize(socket, logger, timeout = nil)
|
|
31
|
-
@header =
|
|
30
|
+
@header = String.new
|
|
32
31
|
@version = nil
|
|
33
32
|
@logger = logger
|
|
34
33
|
negotiate!(socket, timeout)
|
|
35
34
|
end
|
|
36
|
-
|
|
35
|
+
|
|
37
36
|
private
|
|
38
|
-
|
|
37
|
+
|
|
39
38
|
# Negotiates the SSH protocol to use, via the given socket. If the server
|
|
40
39
|
# reports an incompatible SSH version (e.g., SSH1), this will raise an
|
|
41
40
|
# exception.
|
|
42
41
|
def negotiate!(socket, timeout)
|
|
43
42
|
info { "negotiating protocol version" }
|
|
44
|
-
|
|
43
|
+
|
|
45
44
|
debug { "local is `#{PROTO_VERSION}'" }
|
|
46
45
|
socket.write "#{PROTO_VERSION}\r\n"
|
|
47
46
|
socket.flush
|
|
48
|
-
|
|
47
|
+
|
|
49
48
|
raise Net::SSH::ConnectionTimeout, "timeout during server version negotiating" if timeout && !IO.select([socket], nil, nil, timeout)
|
|
49
|
+
|
|
50
50
|
loop do
|
|
51
|
-
@version =
|
|
51
|
+
@version = String.new
|
|
52
52
|
loop do
|
|
53
53
|
begin
|
|
54
54
|
b = socket.readpartial(1)
|
|
@@ -60,14 +60,15 @@ module Net
|
|
|
60
60
|
break if b == "\n"
|
|
61
61
|
end
|
|
62
62
|
break if @version.match(/^SSH-/)
|
|
63
|
+
|
|
63
64
|
@header << @version
|
|
64
65
|
end
|
|
65
|
-
|
|
66
|
+
|
|
66
67
|
@version.chomp!
|
|
67
68
|
debug { "remote is `#{@version}'" }
|
|
68
|
-
|
|
69
|
+
|
|
69
70
|
raise Net::SSH::Exception, "incompatible SSH version `#{@version}'" unless @version.match(/^SSH-(1\.99|2\.0)-/)
|
|
70
|
-
|
|
71
|
+
|
|
71
72
|
raise Net::SSH::ConnectionTimeout, "timeout during client version negotiating" if timeout && !IO.select(nil, [socket], nil, timeout)
|
|
72
73
|
end
|
|
73
74
|
end
|
|
@@ -15,7 +15,6 @@ require 'net/ssh/verifiers/never'
|
|
|
15
15
|
module Net
|
|
16
16
|
module SSH
|
|
17
17
|
module Transport
|
|
18
|
-
|
|
19
18
|
# The transport layer represents the lowest level of the SSH protocol, and
|
|
20
19
|
# implements basic message exchanging and protocol initialization. It will
|
|
21
20
|
# never be instantiated directly (unless you really know what you're about),
|
|
@@ -56,7 +55,7 @@ module Net
|
|
|
56
55
|
# Instantiates a new transport layer abstraction. This will block until
|
|
57
56
|
# the initial key exchange completes, leaving you with a ready-to-use
|
|
58
57
|
# transport session.
|
|
59
|
-
def initialize(host, options={})
|
|
58
|
+
def initialize(host, options = {})
|
|
60
59
|
self.logger = options[:logger]
|
|
61
60
|
|
|
62
61
|
@host = host
|
|
@@ -160,6 +159,7 @@ module Net
|
|
|
160
159
|
# one is performed, causing this method to block until it completes.
|
|
161
160
|
def rekey_as_needed
|
|
162
161
|
return if algorithms.pending?
|
|
162
|
+
|
|
163
163
|
socket.if_needs_rekey? { rekey! }
|
|
164
164
|
end
|
|
165
165
|
|
|
@@ -186,11 +186,11 @@ module Net
|
|
|
186
186
|
# received, it will be enqueued and otherwise ignored. When a key-exchange
|
|
187
187
|
# is not in process, and consume_queue is true, packets will be first
|
|
188
188
|
# read from the queue before the socket is queried.
|
|
189
|
-
def poll_message(mode
|
|
189
|
+
def poll_message(mode = :nonblock, consume_queue = true)
|
|
190
190
|
loop do
|
|
191
191
|
return @queue.shift if consume_queue && @queue.any? && algorithms.allow?(@queue.first)
|
|
192
192
|
|
|
193
|
-
packet = socket.next_packet(mode)
|
|
193
|
+
packet = socket.next_packet(mode, options[:timeout])
|
|
194
194
|
return nil if packet.nil?
|
|
195
195
|
|
|
196
196
|
case packet.type
|
|
@@ -211,6 +211,7 @@ module Net
|
|
|
211
211
|
|
|
212
212
|
else
|
|
213
213
|
return packet if algorithms.allow?(packet)
|
|
214
|
+
|
|
214
215
|
push(packet)
|
|
215
216
|
end
|
|
216
217
|
end
|
|
@@ -222,6 +223,7 @@ module Net
|
|
|
222
223
|
def wait
|
|
223
224
|
loop do
|
|
224
225
|
break if block_given? && yield
|
|
226
|
+
|
|
225
227
|
message = poll_message(:nonblock, false)
|
|
226
228
|
push(message) if message
|
|
227
229
|
break if !block_given?
|
|
@@ -250,30 +252,47 @@ module Net
|
|
|
250
252
|
# Configure's the packet stream's client state with the given set of
|
|
251
253
|
# options. This is typically used to define the cipher, compression, and
|
|
252
254
|
# hmac algorithms to use when sending packets to the server.
|
|
253
|
-
def configure_client(options={})
|
|
255
|
+
def configure_client(options = {})
|
|
254
256
|
socket.client.set(options)
|
|
255
257
|
end
|
|
256
258
|
|
|
257
259
|
# Configure's the packet stream's server state with the given set of
|
|
258
260
|
# options. This is typically used to define the cipher, compression, and
|
|
259
261
|
# hmac algorithms to use when reading packets from the server.
|
|
260
|
-
def configure_server(options={})
|
|
262
|
+
def configure_server(options = {})
|
|
261
263
|
socket.server.set(options)
|
|
262
264
|
end
|
|
263
265
|
|
|
264
266
|
# Sets a new hint for the packet stream, which the packet stream may use
|
|
265
267
|
# to change its behavior. (See PacketStream#hints).
|
|
266
|
-
def hint(which, value=true)
|
|
268
|
+
def hint(which, value = true)
|
|
267
269
|
socket.hints[which] = value
|
|
268
270
|
end
|
|
269
271
|
|
|
270
272
|
public
|
|
271
273
|
|
|
272
274
|
# this method is primarily for use in tests
|
|
273
|
-
attr_reader :queue
|
|
275
|
+
attr_reader :queue # :nodoc:
|
|
274
276
|
|
|
275
277
|
private
|
|
276
278
|
|
|
279
|
+
# Compatibility verifier which allows users to keep using
|
|
280
|
+
# custom verifier code without adding new :verify_signature
|
|
281
|
+
# method.
|
|
282
|
+
class CompatibleVerifier
|
|
283
|
+
def initialize(verifier)
|
|
284
|
+
@verifier = verifier
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def verify(arguments)
|
|
288
|
+
@verifier.verify(arguments)
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def verify_signature(&block)
|
|
292
|
+
yield
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
|
|
277
296
|
# Instantiates a new host-key verification class, based on the value of
|
|
278
297
|
# the parameter.
|
|
279
298
|
#
|
|
@@ -285,8 +304,8 @@ module Net
|
|
|
285
304
|
# - :accept_new (insecure)
|
|
286
305
|
# - :always (secure)
|
|
287
306
|
#
|
|
288
|
-
# If the argument happens to respond to :verify
|
|
289
|
-
# directly. Otherwise, an exception is raised.
|
|
307
|
+
# If the argument happens to respond to :verify and :verify_signature,
|
|
308
|
+
# it is returned directly. Otherwise, an exception is raised.
|
|
290
309
|
#
|
|
291
310
|
# Values false, true, and :very were deprecated in
|
|
292
311
|
# [#595](https://github.com/net-ssh/net-ssh/pull/595)
|
|
@@ -314,7 +333,12 @@ module Net
|
|
|
314
333
|
Net::SSH::Verifiers::Always.new
|
|
315
334
|
else
|
|
316
335
|
if verifier.respond_to?(:verify)
|
|
317
|
-
verifier
|
|
336
|
+
if verifier.respond_to?(:verify_signature)
|
|
337
|
+
verifier
|
|
338
|
+
else
|
|
339
|
+
Kernel.warn("Warning: verifier without :verify_signature is deprecated")
|
|
340
|
+
CompatibleVerifier.new(verifier)
|
|
341
|
+
end
|
|
318
342
|
else
|
|
319
343
|
raise(
|
|
320
344
|
ArgumentError,
|
|
@@ -2,10 +2,9 @@ require 'zlib'
|
|
|
2
2
|
require 'net/ssh/transport/cipher_factory'
|
|
3
3
|
require 'net/ssh/transport/hmac'
|
|
4
4
|
|
|
5
|
-
module Net
|
|
6
|
-
module SSH
|
|
5
|
+
module Net
|
|
6
|
+
module SSH
|
|
7
7
|
module Transport
|
|
8
|
-
|
|
9
8
|
# Encapsulates state information about one end of an SSH connection. Such
|
|
10
9
|
# state includes the packet sequence number, the algorithms in use, how
|
|
11
10
|
# many packets and blocks have been processed since the last reset, and so
|
|
@@ -14,46 +13,46 @@ module Net
|
|
|
14
13
|
class State
|
|
15
14
|
# The socket object that owns this state object.
|
|
16
15
|
attr_reader :socket
|
|
17
|
-
|
|
16
|
+
|
|
18
17
|
# The next packet sequence number for this socket endpoint.
|
|
19
18
|
attr_reader :sequence_number
|
|
20
|
-
|
|
19
|
+
|
|
21
20
|
# The hmac algorithm in use for this endpoint.
|
|
22
21
|
attr_reader :hmac
|
|
23
|
-
|
|
22
|
+
|
|
24
23
|
# The compression algorithm in use for this endpoint.
|
|
25
24
|
attr_reader :compression
|
|
26
|
-
|
|
25
|
+
|
|
27
26
|
# The compression level to use when compressing data (or nil, for the default).
|
|
28
27
|
attr_reader :compression_level
|
|
29
|
-
|
|
28
|
+
|
|
30
29
|
# The number of packets processed since the last call to #reset!
|
|
31
30
|
attr_reader :packets
|
|
32
|
-
|
|
31
|
+
|
|
33
32
|
# The number of data blocks processed since the last call to #reset!
|
|
34
33
|
attr_reader :blocks
|
|
35
|
-
|
|
34
|
+
|
|
36
35
|
# The cipher algorithm in use for this socket endpoint.
|
|
37
36
|
attr_reader :cipher
|
|
38
|
-
|
|
37
|
+
|
|
39
38
|
# The block size for the cipher
|
|
40
39
|
attr_reader :block_size
|
|
41
|
-
|
|
40
|
+
|
|
42
41
|
# The role that this state plays (either :client or :server)
|
|
43
42
|
attr_reader :role
|
|
44
|
-
|
|
43
|
+
|
|
45
44
|
# The maximum number of packets that this endpoint wants to process before
|
|
46
45
|
# needing a rekey.
|
|
47
46
|
attr_accessor :max_packets
|
|
48
|
-
|
|
47
|
+
|
|
49
48
|
# The maximum number of blocks that this endpoint wants to process before
|
|
50
49
|
# needing a rekey.
|
|
51
50
|
attr_accessor :max_blocks
|
|
52
|
-
|
|
51
|
+
|
|
53
52
|
# The user-specified maximum number of bytes that this endpoint ought to
|
|
54
53
|
# process before needing a rekey.
|
|
55
54
|
attr_accessor :rekey_limit
|
|
56
|
-
|
|
55
|
+
|
|
57
56
|
# Creates a new state object, belonging to the given socket. Initializes
|
|
58
57
|
# the algorithms to "none".
|
|
59
58
|
def initialize(socket, role)
|
|
@@ -65,9 +64,9 @@ module Net
|
|
|
65
64
|
@hmac = HMAC.get("none")
|
|
66
65
|
@compression = nil
|
|
67
66
|
@compressor = @decompressor = nil
|
|
68
|
-
@next_iv =
|
|
67
|
+
@next_iv = String.new
|
|
69
68
|
end
|
|
70
|
-
|
|
69
|
+
|
|
71
70
|
# A convenience method for quickly setting multiple values in a single
|
|
72
71
|
# command.
|
|
73
72
|
def set(values)
|
|
@@ -76,19 +75,19 @@ module Net
|
|
|
76
75
|
end
|
|
77
76
|
reset!
|
|
78
77
|
end
|
|
79
|
-
|
|
78
|
+
|
|
80
79
|
def update_cipher(data)
|
|
81
80
|
result = cipher.update(data)
|
|
82
81
|
update_next_iv(role == :client ? result : data)
|
|
83
82
|
return result
|
|
84
83
|
end
|
|
85
|
-
|
|
84
|
+
|
|
86
85
|
def final_cipher
|
|
87
86
|
result = cipher.final
|
|
88
87
|
update_next_iv(role == :client ? result : "", true)
|
|
89
88
|
return result
|
|
90
89
|
end
|
|
91
|
-
|
|
90
|
+
|
|
92
91
|
# Increments the counters. The sequence number is incremented (and remapped
|
|
93
92
|
# so it always fits in a 32-bit integer). The number of packets and blocks
|
|
94
93
|
# are also incremented.
|
|
@@ -97,18 +96,18 @@ module Net
|
|
|
97
96
|
@packets += 1
|
|
98
97
|
@blocks += (packet_length + 4) / @block_size
|
|
99
98
|
end
|
|
100
|
-
|
|
99
|
+
|
|
101
100
|
# The compressor object to use when compressing data. This takes into account
|
|
102
101
|
# the desired compression level.
|
|
103
102
|
def compressor
|
|
104
103
|
@compressor ||= Zlib::Deflate.new(compression_level || Zlib::DEFAULT_COMPRESSION)
|
|
105
104
|
end
|
|
106
|
-
|
|
105
|
+
|
|
107
106
|
# The decompressor object to use when decompressing data.
|
|
108
107
|
def decompressor
|
|
109
108
|
@decompressor ||= Zlib::Inflate.new(nil)
|
|
110
109
|
end
|
|
111
|
-
|
|
110
|
+
|
|
112
111
|
# Returns true if data compression/decompression is enabled. This will
|
|
113
112
|
# return true if :standard compression is selected, or if :delayed
|
|
114
113
|
# compression is selected and the :authenticated hint has been received
|
|
@@ -116,33 +115,35 @@ module Net
|
|
|
116
115
|
def compression?
|
|
117
116
|
compression == :standard || (compression == :delayed && socket.hints[:authenticated])
|
|
118
117
|
end
|
|
119
|
-
|
|
118
|
+
|
|
120
119
|
# Compresses the data. If no compression is in effect, this will just return
|
|
121
120
|
# the data unmodified, otherwise it uses #compressor to compress the data.
|
|
122
121
|
def compress(data)
|
|
123
122
|
data = data.to_s
|
|
124
123
|
return data unless compression?
|
|
124
|
+
|
|
125
125
|
compressor.deflate(data, Zlib::SYNC_FLUSH)
|
|
126
126
|
end
|
|
127
|
-
|
|
127
|
+
|
|
128
128
|
# Deompresses the data. If no compression is in effect, this will just return
|
|
129
129
|
# the data unmodified, otherwise it uses #decompressor to decompress the data.
|
|
130
130
|
def decompress(data)
|
|
131
131
|
data = data.to_s
|
|
132
132
|
return data unless compression?
|
|
133
|
+
|
|
133
134
|
decompressor.inflate(data)
|
|
134
135
|
end
|
|
135
|
-
|
|
136
|
+
|
|
136
137
|
# Resets the counters on the state object, but leaves the sequence_number
|
|
137
138
|
# unchanged. It also sets defaults for and recomputes the max_packets and
|
|
138
139
|
# max_blocks values.
|
|
139
140
|
def reset!
|
|
140
141
|
@packets = @blocks = 0
|
|
141
|
-
|
|
142
|
+
|
|
142
143
|
@max_packets ||= 1 << 31
|
|
143
|
-
|
|
144
|
-
@block_size = cipher.
|
|
145
|
-
|
|
144
|
+
|
|
145
|
+
@block_size = cipher.block_size
|
|
146
|
+
|
|
146
147
|
if max_blocks.nil?
|
|
147
148
|
# cargo-culted from openssh. the idea is that "the 2^(blocksize*2)
|
|
148
149
|
# limit is too expensive for 3DES, blowfish, etc., so enforce a 1GB
|
|
@@ -152,16 +153,16 @@ module Net
|
|
|
152
153
|
else
|
|
153
154
|
@max_blocks = (1 << 30) / @block_size
|
|
154
155
|
end
|
|
155
|
-
|
|
156
|
+
|
|
156
157
|
# if a limit on the # of bytes has been given, convert that into a
|
|
157
158
|
# minimum number of blocks processed.
|
|
158
|
-
|
|
159
|
+
|
|
159
160
|
@max_blocks = [@max_blocks, rekey_limit / @block_size].min if rekey_limit
|
|
160
161
|
end
|
|
161
|
-
|
|
162
|
+
|
|
162
163
|
cleanup
|
|
163
164
|
end
|
|
164
|
-
|
|
165
|
+
|
|
165
166
|
# Closes any the compressor and/or decompressor objects that have been
|
|
166
167
|
# instantiated.
|
|
167
168
|
def cleanup
|
|
@@ -169,17 +170,17 @@ module Net
|
|
|
169
170
|
@compressor.finish if !@compressor.finished?
|
|
170
171
|
@compressor.close
|
|
171
172
|
end
|
|
172
|
-
|
|
173
|
+
|
|
173
174
|
if @decompressor
|
|
174
175
|
# we call reset here so that we don't get warnings when we try to
|
|
175
176
|
# close the decompressor
|
|
176
177
|
@decompressor.reset
|
|
177
178
|
@decompressor.close
|
|
178
179
|
end
|
|
179
|
-
|
|
180
|
+
|
|
180
181
|
@compressor = @decompressor = nil
|
|
181
182
|
end
|
|
182
|
-
|
|
183
|
+
|
|
183
184
|
# Returns true if the number of packets processed exceeds the maximum
|
|
184
185
|
# number of packets, or if the number of blocks processed exceeds the
|
|
185
186
|
# maximum number of blocks.
|
|
@@ -187,22 +188,21 @@ module Net
|
|
|
187
188
|
max_packets && packets > max_packets ||
|
|
188
189
|
max_blocks && blocks > max_blocks
|
|
189
190
|
end
|
|
190
|
-
|
|
191
|
+
|
|
191
192
|
private
|
|
192
|
-
|
|
193
|
-
def update_next_iv(data, reset=false)
|
|
193
|
+
|
|
194
|
+
def update_next_iv(data, reset = false)
|
|
194
195
|
@next_iv << data
|
|
195
196
|
@next_iv = @next_iv[@next_iv.size - cipher.iv_len..-1]
|
|
196
|
-
|
|
197
|
+
|
|
197
198
|
if reset
|
|
198
199
|
cipher.reset
|
|
199
200
|
cipher.iv = @next_iv
|
|
200
201
|
end
|
|
201
|
-
|
|
202
|
+
|
|
202
203
|
return data
|
|
203
204
|
end
|
|
204
205
|
end
|
|
205
|
-
|
|
206
206
|
end
|
|
207
207
|
end
|
|
208
208
|
end
|
|
@@ -5,7 +5,6 @@ require 'net/ssh/verifiers/always'
|
|
|
5
5
|
module Net
|
|
6
6
|
module SSH
|
|
7
7
|
module Verifiers
|
|
8
|
-
|
|
9
8
|
# Does a strict host verification, looking the server up in the known
|
|
10
9
|
# host files to see if a key has already been seen for this server. If this
|
|
11
10
|
# server does not appear in any host file, this will silently add the
|
|
@@ -21,8 +20,14 @@ module Net
|
|
|
21
20
|
return true
|
|
22
21
|
end
|
|
23
22
|
end
|
|
24
|
-
end
|
|
25
23
|
|
|
24
|
+
def verify_signature(&block)
|
|
25
|
+
yield
|
|
26
|
+
rescue HostKeyUnknown => err
|
|
27
|
+
err.remember_host!
|
|
28
|
+
return true
|
|
29
|
+
end
|
|
30
|
+
end
|
|
26
31
|
end
|
|
27
32
|
end
|
|
28
33
|
end
|
|
@@ -3,7 +3,6 @@ require 'net/ssh/verifiers/accept_new'
|
|
|
3
3
|
module Net
|
|
4
4
|
module SSH
|
|
5
5
|
module Verifiers
|
|
6
|
-
|
|
7
6
|
# Basically the same as the AcceptNew verifier, but does not try to actually
|
|
8
7
|
# verify a connection if the server is the localhost and the port is a
|
|
9
8
|
# nonstandard port number. Those two conditions will typically mean the
|
|
@@ -14,6 +13,7 @@ module Net
|
|
|
14
13
|
# returns true. Otherwise, performs the standard strict verification.
|
|
15
14
|
def verify(arguments)
|
|
16
15
|
return true if tunnelled?(arguments)
|
|
16
|
+
|
|
17
17
|
super
|
|
18
18
|
end
|
|
19
19
|
|
|
@@ -28,7 +28,6 @@ module Net
|
|
|
28
28
|
return ip == "127.0.0.1" || ip == "::1"
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
|
-
|
|
32
31
|
end
|
|
33
32
|
end
|
|
34
33
|
end
|