net-ssh 8.0.0.rc3 → 8.0.0.rc5
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 +4 -1
- data/.github/pull_request_template.md +1 -0
- data/.github/workflows/changelog.yml +43 -0
- data/CHANGES.txt +40 -0
- data/CONTRIBUTING.md +35 -0
- data/README.md +65 -48
- data/Rakefile +48 -0
- data/changelog.d/README.md +1 -0
- data/lib/net/ssh/authentication/agent.rb +4 -1
- data/lib/net/ssh/authentication/ed25519.rb +1 -1
- data/lib/net/ssh/authentication/key_manager.rb +22 -12
- data/lib/net/ssh/authentication/methods/abstract.rb +2 -8
- data/lib/net/ssh/authentication/methods/publickey.rb +3 -1
- data/lib/net/ssh/authentication/security_key.rb +71 -0
- data/lib/net/ssh/authentication/session.rb +29 -17
- data/lib/net/ssh/buffer.rb +6 -1
- data/lib/net/ssh/buffered_io.rb +40 -3
- data/lib/net/ssh/config.rb +16 -1
- data/lib/net/ssh/connection/channel.rb +49 -2
- data/lib/net/ssh/connection/session.rb +58 -8
- data/lib/net/ssh/errors.rb +24 -6
- data/lib/net/ssh/key_factory.rb +7 -1
- data/lib/net/ssh/known_hosts.rb +18 -8
- data/lib/net/ssh/multiplex/client.rb +162 -0
- data/lib/net/ssh/multiplex/control_path.rb +120 -0
- data/lib/net/ssh/multiplex/proxy_transport.rb +218 -0
- data/lib/net/ssh/multiplex.rb +130 -0
- data/lib/net/ssh/proxy/jump.rb +40 -2
- data/lib/net/ssh/service/forward.rb +26 -21
- data/lib/net/ssh/test.rb +2 -1
- data/lib/net/ssh/transport/algorithms.rb +71 -23
- data/lib/net/ssh/transport/hmac/sha1_etm.rb +12 -0
- data/lib/net/ssh/transport/hmac.rb +2 -0
- data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +8 -0
- data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +45 -15
- data/lib/net/ssh/transport/openssl.rb +2 -1
- data/lib/net/ssh/transport/session.rb +21 -44
- data/lib/net/ssh/verifiers/always.rb +0 -3
- data/lib/net/ssh/version.rb +1 -1
- data/lib/net/ssh.rb +132 -56
- data/net-ssh.gemspec +1 -1
- data/rakelib/changelog.rb +229 -0
- data.tar.gz.sig +0 -0
- metadata +23 -6
- metadata.gz.sig +0 -0
|
@@ -320,6 +320,11 @@ module Net
|
|
|
320
320
|
session.listen_to(client)
|
|
321
321
|
channel[:socket] = client
|
|
322
322
|
|
|
323
|
+
# Grant window only for data the local end has accepted, so a slow
|
|
324
|
+
# reader stalls the remote sender instead of filling our memory.
|
|
325
|
+
channel.grant_window_on_consume!
|
|
326
|
+
client.on_sent { |bytes| channel.consumed(bytes) }
|
|
327
|
+
|
|
323
328
|
channel.on_data do |ch, data|
|
|
324
329
|
debug { "data:#{data.length} on #{type} forwarded channel" }
|
|
325
330
|
ch[:socket].enqueue(data)
|
|
@@ -327,36 +332,36 @@ module Net
|
|
|
327
332
|
|
|
328
333
|
channel.on_eof do |ch|
|
|
329
334
|
debug { "eof #{type} on #{type} forwarded channel" }
|
|
330
|
-
|
|
331
|
-
ch[:socket].send_pending
|
|
332
|
-
ch[:socket].shutdown Socket::SHUT_WR
|
|
333
|
-
rescue IOError => e
|
|
334
|
-
if e.message =~ /closed/ then
|
|
335
|
-
debug { "epipe in on_eof => shallowing exception:#{e}" }
|
|
336
|
-
else
|
|
337
|
-
raise
|
|
338
|
-
end
|
|
339
|
-
rescue Errno::EPIPE => e
|
|
340
|
-
debug { "epipe in on_eof => shallowing exception:#{e}" }
|
|
341
|
-
rescue Errno::ENOTCONN => e
|
|
342
|
-
debug { "enotconn in on_eof => shallowing exception:#{e}" }
|
|
343
|
-
end
|
|
335
|
+
ch[:socket].shutdown_write_when_drained
|
|
344
336
|
end
|
|
345
337
|
|
|
346
338
|
channel.on_close do |ch|
|
|
347
339
|
debug { "closing #{type} forwarded channel" }
|
|
348
|
-
ch[:socket]
|
|
349
|
-
session.stop_listening_to(ch[:socket])
|
|
340
|
+
session.close_when_drained(ch[:socket])
|
|
350
341
|
end
|
|
351
342
|
|
|
352
343
|
channel.on_process do |ch|
|
|
353
|
-
|
|
344
|
+
socket = ch[:socket]
|
|
345
|
+
if socket.closed?
|
|
354
346
|
ch.info { "#{type} forwarded connection closed" }
|
|
347
|
+
session.stop_listening_to(socket)
|
|
355
348
|
ch.close
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
349
|
+
else
|
|
350
|
+
if socket.available > 0
|
|
351
|
+
data = socket.read_available(8192)
|
|
352
|
+
ch.debug { "read #{data.length} bytes from client, sending over #{type} forwarded connection" }
|
|
353
|
+
ch.send_data(data)
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
# Like OpenSSH's channel_pre_open, only read more once the remote
|
|
357
|
+
# window can take what is already queued; writes to the socket keep
|
|
358
|
+
# flowing. ch.output is still unsent here because Channel#process
|
|
359
|
+
# runs this block before enqueue_pending_output.
|
|
360
|
+
if ch.output.length + socket.available < ch.remote_window_size.to_i
|
|
361
|
+
session.resume_reading(socket)
|
|
362
|
+
else
|
|
363
|
+
session.pause_reading(socket)
|
|
364
|
+
end
|
|
360
365
|
end
|
|
361
366
|
end
|
|
362
367
|
end
|
data/lib/net/ssh/test.rb
CHANGED
|
@@ -73,7 +73,8 @@ module Net
|
|
|
73
73
|
def transport(options = {})
|
|
74
74
|
@transport ||= Net::SSH::Transport::Session.new(
|
|
75
75
|
options[:host] || "localhost",
|
|
76
|
-
options.merge(kex: "test", host_key: "ssh-rsa",
|
|
76
|
+
options.merge(kex: "test", host_key: "ssh-rsa", encryption: "+none", hmac: "+none",
|
|
77
|
+
append_all_supported_algorithms: true, verify_host_key: :never, proxy: socket(options))
|
|
77
78
|
)
|
|
78
79
|
end
|
|
79
80
|
|
|
@@ -8,6 +8,7 @@ require 'net/ssh/transport/kex'
|
|
|
8
8
|
require 'net/ssh/transport/kex/curve25519_sha256_loader'
|
|
9
9
|
require 'net/ssh/transport/server_version'
|
|
10
10
|
require 'net/ssh/authentication/ed25519_loader'
|
|
11
|
+
require 'net/ssh/authentication/security_key'
|
|
11
12
|
|
|
12
13
|
module Net
|
|
13
14
|
module SSH
|
|
@@ -19,7 +20,7 @@ module Net
|
|
|
19
20
|
#
|
|
20
21
|
# You will never instantiate or reference this directly. It is used
|
|
21
22
|
# internally by the transport layer.
|
|
22
|
-
class Algorithms
|
|
23
|
+
class Algorithms # rubocop:disable Metrics/ClassLength
|
|
23
24
|
include Loggable
|
|
24
25
|
include Constants
|
|
25
26
|
|
|
@@ -31,18 +32,14 @@ module Net
|
|
|
31
32
|
ecdsa-sha2-nistp521
|
|
32
33
|
ecdsa-sha2-nistp384
|
|
33
34
|
ecdsa-sha2-nistp256
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
ssh-rsa
|
|
37
|
-
rsa-sha2-256
|
|
38
|
-
rsa-sha2-512],
|
|
35
|
+
rsa-sha2-512
|
|
36
|
+
rsa-sha2-256],
|
|
39
37
|
|
|
40
38
|
kex: %w[ecdh-sha2-nistp521
|
|
41
39
|
ecdh-sha2-nistp384
|
|
42
40
|
ecdh-sha2-nistp256
|
|
43
41
|
diffie-hellman-group-exchange-sha256
|
|
44
|
-
diffie-hellman-group14-sha256
|
|
45
|
-
diffie-hellman-group14-sha1],
|
|
42
|
+
diffie-hellman-group14-sha256],
|
|
46
43
|
|
|
47
44
|
encryption: %w[aes256-ctr
|
|
48
45
|
aes192-ctr
|
|
@@ -51,8 +48,18 @@ module Net
|
|
|
51
48
|
aes128-gcm@openssh.com],
|
|
52
49
|
|
|
53
50
|
hmac: %w[hmac-sha2-512-etm@openssh.com hmac-sha2-256-etm@openssh.com
|
|
51
|
+
hmac-sha1-etm@openssh.com
|
|
54
52
|
hmac-sha2-512 hmac-sha2-256
|
|
55
|
-
hmac-sha1]
|
|
53
|
+
hmac-sha1],
|
|
54
|
+
|
|
55
|
+
pubkey_algorithms: %w[ssh-ed25519-cert-v01@openssh.com
|
|
56
|
+
ecdsa-sha2-nistp256-cert-v01@openssh.com ecdsa-sha2-nistp384-cert-v01@openssh.com
|
|
57
|
+
ecdsa-sha2-nistp521-cert-v01@openssh.com
|
|
58
|
+
rsa-sha2-512-cert-v01@openssh.com rsa-sha2-256-cert-v01@openssh.com
|
|
59
|
+
ssh-ed25519
|
|
60
|
+
ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521] +
|
|
61
|
+
[Authentication::SecurityKey::SK_SSH_ED25519, Authentication::SecurityKey::SK_ECDSA_SHA2_NISTP256] +
|
|
62
|
+
%w[rsa-sha2-512 rsa-sha2-256]
|
|
56
63
|
}.freeze
|
|
57
64
|
|
|
58
65
|
if Net::SSH::Transport::ChaCha20Poly1305CipherLoader::LOADED
|
|
@@ -76,10 +83,12 @@ module Net
|
|
|
76
83
|
|
|
77
84
|
# Define all algorithms, with the deprecated, supported by Net::SSH.
|
|
78
85
|
ALGORITHMS = {
|
|
79
|
-
host_key: DEFAULT_ALGORITHMS[:host_key] +
|
|
86
|
+
host_key: DEFAULT_ALGORITHMS[:host_key] +
|
|
87
|
+
%w[ssh-rsa-cert-v01@openssh.com ssh-rsa-cert-v00@openssh.com ssh-rsa ssh-dss],
|
|
80
88
|
|
|
81
89
|
kex: DEFAULT_ALGORITHMS[:kex] +
|
|
82
|
-
%w[diffie-hellman-
|
|
90
|
+
%w[diffie-hellman-group14-sha1
|
|
91
|
+
diffie-hellman-group-exchange-sha1
|
|
83
92
|
diffie-hellman-group1-sha1],
|
|
84
93
|
|
|
85
94
|
encryption: DEFAULT_ALGORITHMS[:encryption] +
|
|
@@ -98,10 +107,20 @@ module Net
|
|
|
98
107
|
hmac-md5 hmac-md5-96
|
|
99
108
|
none],
|
|
100
109
|
|
|
110
|
+
pubkey_algorithms: DEFAULT_ALGORITHMS[:pubkey_algorithms] + %w[ssh-rsa-cert-v01@openssh.com ssh-rsa ssh-dss],
|
|
111
|
+
|
|
101
112
|
compression: %w[none zlib@openssh.com zlib],
|
|
102
113
|
language: %w[]
|
|
103
114
|
}.freeze
|
|
104
115
|
|
|
116
|
+
# Not added by append_all_supported_algorithms; only used when named explicitly.
|
|
117
|
+
EXPLICIT_ONLY_ALGORITHMS = {
|
|
118
|
+
encryption: %w[none],
|
|
119
|
+
hmac: %w[none]
|
|
120
|
+
}.freeze
|
|
121
|
+
|
|
122
|
+
DEPRECATED_HOST_KEY = 'ssh-rsa-cert-v00@openssh.com'.freeze
|
|
123
|
+
|
|
105
124
|
# The underlying transport layer session that supports this object
|
|
106
125
|
attr_reader :session
|
|
107
126
|
|
|
@@ -269,11 +288,16 @@ module Net
|
|
|
269
288
|
|
|
270
289
|
ALGORITHMS.each do |algorithm, supported|
|
|
271
290
|
algorithms[algorithm] = compose_algorithm_list(
|
|
272
|
-
supported,
|
|
273
|
-
|
|
291
|
+
supported,
|
|
292
|
+
DEFAULT_ALGORITHMS[algorithm],
|
|
293
|
+
options[algorithm] || DEFAULT_ALGORITHMS[algorithm],
|
|
294
|
+
options[:append_all_supported_algorithms],
|
|
295
|
+
supported - EXPLICIT_ONLY_ALGORITHMS.fetch(algorithm, [])
|
|
274
296
|
)
|
|
275
297
|
end
|
|
276
298
|
|
|
299
|
+
warn_deprecated_host_key if host_key_explicitly_named?(DEPRECATED_HOST_KEY)
|
|
300
|
+
|
|
277
301
|
# for convention, make sure our list has the same keys as the server
|
|
278
302
|
# list
|
|
279
303
|
|
|
@@ -287,7 +311,11 @@ module Net
|
|
|
287
311
|
# existing known key for the host has preference.
|
|
288
312
|
|
|
289
313
|
existing_keys = session.host_keys
|
|
290
|
-
host_keys = existing_keys.flat_map { |key| key.respond_to?(:ssh_types) ? key.ssh_types : [key.ssh_type] }
|
|
314
|
+
host_keys = existing_keys.flat_map { |key| key.respond_to?(:ssh_types) ? key.ssh_types : [key.ssh_type] }
|
|
315
|
+
# A known ssh-rsa key also verifies with rsa-sha2-*, and must not re-enable disabled SHA-1 algorithms.
|
|
316
|
+
disabled_sha1 = %w[ssh-rsa ssh-rsa-cert-v01@openssh.com ssh-rsa-cert-v00@openssh.com] - algorithms[:host_key]
|
|
317
|
+
host_keys = host_keys.flat_map { |type| type == 'ssh-rsa' ? %w[rsa-sha2-512 rsa-sha2-256 ssh-rsa] : [type] }
|
|
318
|
+
host_keys = (host_keys - disabled_sha1).uniq
|
|
291
319
|
algorithms[:host_key].each do |name|
|
|
292
320
|
host_keys << name unless host_keys.include?(name)
|
|
293
321
|
end
|
|
@@ -296,21 +324,27 @@ module Net
|
|
|
296
324
|
end
|
|
297
325
|
|
|
298
326
|
# Composes the list of algorithms by taking supported algorithms and matching with supplied options.
|
|
299
|
-
def compose_algorithm_list(supported, option, append_all_supported_algorithms = false)
|
|
327
|
+
def compose_algorithm_list(supported, defaults, option, append_all_supported_algorithms = false, appendable = supported)
|
|
300
328
|
return supported.dup unless option
|
|
301
329
|
|
|
302
330
|
list = []
|
|
303
331
|
option = Array(option).compact.uniq
|
|
304
332
|
|
|
305
|
-
if option.first && option.first.start_with?('+', '-')
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
333
|
+
if option.first && option.first.start_with?('+', '-', '^')
|
|
334
|
+
ops = { '^' => [], '+' => [], '-' => [] }
|
|
335
|
+
cur_list = nil
|
|
336
|
+
option.each do |cur_opt|
|
|
337
|
+
if ops.key?(cur_opt[0])
|
|
338
|
+
cur_list = ops[cur_opt[0]]
|
|
339
|
+
cur_opt = cur_opt[1..-1]
|
|
340
|
+
end
|
|
341
|
+
cur_list << cur_opt
|
|
342
|
+
end
|
|
310
343
|
|
|
311
|
-
list
|
|
344
|
+
list = ops['^'] + defaults + ops['+']
|
|
345
|
+
list.concat(appendable) if append_all_supported_algorithms
|
|
312
346
|
|
|
313
|
-
|
|
347
|
+
ops['-'].each do |opt|
|
|
314
348
|
if opt.include?('*')
|
|
315
349
|
opt_escaped = Regexp.escape(opt)
|
|
316
350
|
algo_re = /\A#{opt_escaped.gsub('\*', '[A-Za-z\d\-@\.]*')}\z/
|
|
@@ -325,7 +359,7 @@ module Net
|
|
|
325
359
|
list = option
|
|
326
360
|
|
|
327
361
|
if append_all_supported_algorithms
|
|
328
|
-
|
|
362
|
+
appendable.each { |name| list << name unless list.include?(name) }
|
|
329
363
|
end
|
|
330
364
|
end
|
|
331
365
|
|
|
@@ -341,6 +375,19 @@ module Net
|
|
|
341
375
|
list
|
|
342
376
|
end
|
|
343
377
|
|
|
378
|
+
def host_key_explicitly_named?(name)
|
|
379
|
+
algorithms[:host_key].include?(name) &&
|
|
380
|
+
Array(options[:host_key]).any? { |opt| opt.to_s.delete_prefix('+').delete_prefix('^') == name }
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
def warn_deprecated_host_key
|
|
384
|
+
return if @deprecated_host_key_warned
|
|
385
|
+
|
|
386
|
+
@deprecated_host_key_warned = true
|
|
387
|
+
Kernel.warn("#{DEPRECATED_HOST_KEY} is deprecated and will be removed in a future version; " \
|
|
388
|
+
"net-ssh cannot parse v00 certificates, use ssh-rsa-cert-v01@openssh.com or a modern host key type instead")
|
|
389
|
+
end
|
|
390
|
+
|
|
344
391
|
# Parses a KEXINIT packet from the server.
|
|
345
392
|
def parse_server_algorithm_packet(packet)
|
|
346
393
|
data = { raw: packet.content }
|
|
@@ -398,6 +445,7 @@ module Net
|
|
|
398
445
|
def negotiate_algorithms
|
|
399
446
|
@kex = negotiate(:kex)
|
|
400
447
|
@host_key = negotiate(:host_key)
|
|
448
|
+
warn_deprecated_host_key if @host_key == DEPRECATED_HOST_KEY
|
|
401
449
|
@encryption_client = negotiate(:encryption_client)
|
|
402
450
|
@encryption_server = negotiate(:encryption_server)
|
|
403
451
|
@hmac_client = negotiate(:hmac_client)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require 'net/ssh/transport/hmac/abstract'
|
|
2
|
+
|
|
3
|
+
module Net::SSH::Transport::HMAC
|
|
4
|
+
# The SHA1 Encrypt-Then-Mac HMAC algorithm. This has a mac and
|
|
5
|
+
# key length of 20, and uses the SHA1 digest algorithm.
|
|
6
|
+
class SHA1_Etm < Abstract
|
|
7
|
+
etm true
|
|
8
|
+
mac_length 20
|
|
9
|
+
key_length 20
|
|
10
|
+
digest_class OpenSSL::Digest::SHA1
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -9,6 +9,7 @@ require 'net/ssh/transport/hmac/sha2_512'
|
|
|
9
9
|
require 'net/ssh/transport/hmac/sha2_512_96'
|
|
10
10
|
require 'net/ssh/transport/hmac/sha2_256_etm'
|
|
11
11
|
require 'net/ssh/transport/hmac/sha2_512_etm'
|
|
12
|
+
require 'net/ssh/transport/hmac/sha1_etm'
|
|
12
13
|
require 'net/ssh/transport/hmac/ripemd160'
|
|
13
14
|
require 'net/ssh/transport/hmac/none'
|
|
14
15
|
|
|
@@ -27,6 +28,7 @@ module Net::SSH::Transport::HMAC
|
|
|
27
28
|
'hmac-sha2-512-96' => SHA2_512_96,
|
|
28
29
|
'hmac-sha2-256-etm@openssh.com' => SHA2_256_Etm,
|
|
29
30
|
'hmac-sha2-512-etm@openssh.com' => SHA2_512_Etm,
|
|
31
|
+
'hmac-sha1-etm@openssh.com' => SHA1_Etm,
|
|
30
32
|
'hmac-ripemd160' => RIPEMD160,
|
|
31
33
|
'hmac-ripemd160@openssh.com' => RIPEMD160,
|
|
32
34
|
'none' => None
|
|
@@ -102,6 +102,7 @@ module Net
|
|
|
102
102
|
result[:key_blob] = buffer.read_string
|
|
103
103
|
result[:server_key] = Net::SSH::Buffer.new(result[:key_blob]).read_key
|
|
104
104
|
result[:server_dh_pubkey] = buffer.read_bignum
|
|
105
|
+
validate_server_dh_pubkey(result[:server_dh_pubkey])
|
|
105
106
|
result[:shared_secret] = OpenSSL::BN.new(dh.compute_key(result[:server_dh_pubkey]), 2)
|
|
106
107
|
|
|
107
108
|
sig_buffer = Net::SSH::Buffer.new(buffer.read_string)
|
|
@@ -115,6 +116,13 @@ module Net
|
|
|
115
116
|
|
|
116
117
|
return result
|
|
117
118
|
end
|
|
119
|
+
|
|
120
|
+
# Rejects degenerate values that would let the server force a
|
|
121
|
+
# predictable shared secret, as OpenSSH's dh_pub_is_valid.
|
|
122
|
+
def validate_server_dh_pubkey(pub)
|
|
123
|
+
valid = pub > 1 && pub < dh.p - 1 && pub.to_i.to_s(2).count("1") >= 4
|
|
124
|
+
raise Net::SSH::Exception, "server's DH public value is invalid" unless valid
|
|
125
|
+
end
|
|
118
126
|
end
|
|
119
127
|
end
|
|
120
128
|
end
|
|
@@ -6,26 +6,43 @@ module Net::SSH::Transport::Kex
|
|
|
6
6
|
# A key-exchange service implementing the
|
|
7
7
|
# "diffie-hellman-group-exchange-sha1" key-exchange algorithm.
|
|
8
8
|
class DiffieHellmanGroupExchangeSHA1 < DiffieHellmanGroup1SHA1
|
|
9
|
-
MINIMUM_BITS =
|
|
9
|
+
MINIMUM_BITS = 2048
|
|
10
10
|
MAXIMUM_BITS = 8192
|
|
11
|
+
CISCO_MAXIMUM_NEED_BITS = 4096
|
|
11
12
|
|
|
12
13
|
private
|
|
13
14
|
|
|
14
|
-
#
|
|
15
|
+
# Computes the minimum, preferred and maximum group sizes to request,
|
|
16
|
+
# like OpenSSH's kexgex_client.
|
|
15
17
|
def compute_need_bits
|
|
16
|
-
# for Compatibility: OpenSSH requires (need_bits * 2 + 1) length of parameter
|
|
17
|
-
need_bits = data[:need_bytes] * 8 * 2 + 1
|
|
18
|
-
|
|
19
18
|
data[:minimum_dh_bits] ||= MINIMUM_BITS
|
|
19
|
+
data[:maximum_dh_bits] = [MAXIMUM_BITS, data[:minimum_dh_bits]].max
|
|
20
|
+
data[:need_bits] = estimate_group_bits(data[:need_bytes] * 8).clamp(data[:minimum_dh_bits], maximum_need_bits)
|
|
21
|
+
end
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
# Some Cisco servers fail instead of returning their 4096-bit group when asked for more (OpenSSH bz#2209).
|
|
24
|
+
def maximum_need_bits
|
|
25
|
+
return data[:maximum_dh_bits] unless cisco_server?
|
|
26
|
+
|
|
27
|
+
[CISCO_MAXIMUM_NEED_BITS, data[:minimum_dh_bits]].max
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def cisco_server?
|
|
31
|
+
data[:server_version_string].to_s.match?(/\ASSH-[\d.]+-Cisco-1\./)
|
|
32
|
+
end
|
|
26
33
|
|
|
27
|
-
|
|
28
|
-
|
|
34
|
+
# Group size giving at least the given bits of security (NIST SP 800-57),
|
|
35
|
+
# as OpenSSH's dh_estimate.
|
|
36
|
+
def estimate_group_bits(security_bits)
|
|
37
|
+
if security_bits <= 112
|
|
38
|
+
2048
|
|
39
|
+
elsif security_bits <= 128
|
|
40
|
+
3072
|
|
41
|
+
elsif security_bits <= 192
|
|
42
|
+
7680
|
|
43
|
+
else
|
|
44
|
+
8192
|
|
45
|
+
end
|
|
29
46
|
end
|
|
30
47
|
|
|
31
48
|
# Returns the DH key parameters for the given session.
|
|
@@ -34,7 +51,7 @@ module Net::SSH::Transport::Kex
|
|
|
34
51
|
|
|
35
52
|
# request the DH key parameters for the given number of bits.
|
|
36
53
|
buffer = Net::SSH::Buffer.from(:byte, KEXDH_GEX_REQUEST, :long, data[:minimum_dh_bits],
|
|
37
|
-
:long, data[:need_bits], :long,
|
|
54
|
+
:long, data[:need_bits], :long, data[:maximum_dh_bits])
|
|
38
55
|
connection.send_message(buffer)
|
|
39
56
|
|
|
40
57
|
buffer = connection.next_message
|
|
@@ -43,9 +60,22 @@ module Net::SSH::Transport::Kex
|
|
|
43
60
|
p = buffer.read_bignum
|
|
44
61
|
g = buffer.read_bignum
|
|
45
62
|
|
|
63
|
+
validate_group(p, g)
|
|
64
|
+
|
|
46
65
|
[p, g]
|
|
47
66
|
end
|
|
48
67
|
|
|
68
|
+
def validate_group(prime, generator)
|
|
69
|
+
bits = prime.num_bits
|
|
70
|
+
if bits < data[:minimum_dh_bits]
|
|
71
|
+
raise Net::SSH::Exception,
|
|
72
|
+
"server's DH group is #{bits} bits, below the minimum of #{data[:minimum_dh_bits]}; " \
|
|
73
|
+
"pass minimum_dh_bits: #{bits} to allow it"
|
|
74
|
+
end
|
|
75
|
+
raise Net::SSH::Exception, "server's DH group is #{bits} bits, above the maximum of #{data[:maximum_dh_bits]}" if bits > data[:maximum_dh_bits]
|
|
76
|
+
raise Net::SSH::Exception, "server's DH group generator is invalid" unless generator > 1 && generator < prime - 1
|
|
77
|
+
end
|
|
78
|
+
|
|
49
79
|
# Returns the INIT/REPLY constants used by this algorithm.
|
|
50
80
|
def get_message_types
|
|
51
81
|
[KEXDH_GEX_INIT, KEXDH_GEX_REPLY]
|
|
@@ -60,9 +90,9 @@ module Net::SSH::Transport::Kex
|
|
|
60
90
|
data[:client_algorithm_packet],
|
|
61
91
|
data[:server_algorithm_packet],
|
|
62
92
|
result[:key_blob]
|
|
63
|
-
response.write_long
|
|
93
|
+
response.write_long data[:minimum_dh_bits],
|
|
64
94
|
data[:need_bits],
|
|
65
|
-
|
|
95
|
+
data[:maximum_dh_bits]
|
|
66
96
|
response.write_bignum dh.p, dh.g, dh.pub_key,
|
|
67
97
|
result[:server_dh_pubkey],
|
|
68
98
|
result[:shared_secret]
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'openssl'
|
|
2
|
+
require 'net/ssh/errors'
|
|
2
3
|
require 'net/ssh/authentication/pub_key_fingerprint'
|
|
3
4
|
|
|
4
5
|
module OpenSSL
|
|
@@ -178,7 +179,7 @@ module OpenSSL
|
|
|
178
179
|
|
|
179
180
|
return key
|
|
180
181
|
rescue OpenSSL::PKey::ECError
|
|
181
|
-
raise
|
|
182
|
+
raise Net::SSH::UnsupportedKeyType, "unsupported key type `ecdsa-sha2-#{curve_name_in_type}'"
|
|
182
183
|
end
|
|
183
184
|
end
|
|
184
185
|
|
|
@@ -107,6 +107,10 @@ module Net
|
|
|
107
107
|
end
|
|
108
108
|
end
|
|
109
109
|
|
|
110
|
+
def pubkey_algorithms
|
|
111
|
+
algorithms[:pubkey_algorithms]
|
|
112
|
+
end
|
|
113
|
+
|
|
110
114
|
# Returns the host (and possibly IP address) in a format compatible with
|
|
111
115
|
# SSH known-host files.
|
|
112
116
|
def host_as_string
|
|
@@ -313,22 +317,12 @@ module Net
|
|
|
313
317
|
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
|
314
318
|
end
|
|
315
319
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
end
|
|
323
|
-
|
|
324
|
-
def verify(arguments)
|
|
325
|
-
@verifier.verify(arguments)
|
|
326
|
-
end
|
|
327
|
-
|
|
328
|
-
def verify_signature(&block)
|
|
329
|
-
yield
|
|
330
|
-
end
|
|
331
|
-
end
|
|
320
|
+
REMOVED_VERIFY_HOST_KEY_VALUES = {
|
|
321
|
+
false => :never,
|
|
322
|
+
true => :accept_new_or_local_tunnel,
|
|
323
|
+
very: :accept_new,
|
|
324
|
+
secure: :always
|
|
325
|
+
}.freeze
|
|
332
326
|
|
|
333
327
|
# Instantiates a new host-key verification class, based on the value of
|
|
334
328
|
# the parameter.
|
|
@@ -343,46 +337,29 @@ module Net
|
|
|
343
337
|
#
|
|
344
338
|
# If the argument happens to respond to :verify and :verify_signature,
|
|
345
339
|
# it is returned directly. Otherwise, an exception is raised.
|
|
346
|
-
#
|
|
347
|
-
# Values false, true, and :very were deprecated in
|
|
348
|
-
# [#595](https://github.com/net-ssh/net-ssh/pull/595)
|
|
349
340
|
def select_host_key_verifier(verifier)
|
|
350
341
|
case verifier
|
|
351
|
-
when false
|
|
352
|
-
Kernel.warn('verify_host_key: false is deprecated, use :never')
|
|
353
|
-
Net::SSH::Verifiers::Never.new
|
|
354
342
|
when :never then
|
|
355
343
|
Net::SSH::Verifiers::Never.new
|
|
356
|
-
when true
|
|
357
|
-
Kernel.warn('verify_host_key: true is deprecated, use :accept_new_or_local_tunnel')
|
|
358
|
-
Net::SSH::Verifiers::AcceptNewOrLocalTunnel.new
|
|
359
344
|
when :accept_new_or_local_tunnel, nil then
|
|
360
345
|
Net::SSH::Verifiers::AcceptNewOrLocalTunnel.new
|
|
361
|
-
when :very
|
|
362
|
-
Kernel.warn('verify_host_key: :very is deprecated, use :accept_new')
|
|
363
|
-
Net::SSH::Verifiers::AcceptNew.new
|
|
364
346
|
when :accept_new then
|
|
365
347
|
Net::SSH::Verifiers::AcceptNew.new
|
|
366
|
-
when :secure then
|
|
367
|
-
Kernel.warn('verify_host_key: :secure is deprecated, use :always')
|
|
368
|
-
Net::SSH::Verifiers::Always.new
|
|
369
348
|
when :always then
|
|
370
349
|
Net::SSH::Verifiers::Always.new
|
|
350
|
+
when *REMOVED_VERIFY_HOST_KEY_VALUES.keys
|
|
351
|
+
raise ArgumentError,
|
|
352
|
+
"verify_host_key: #{verifier.inspect} has been removed, use #{REMOVED_VERIFY_HOST_KEY_VALUES[verifier].inspect}"
|
|
371
353
|
else
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
end
|
|
379
|
-
else
|
|
380
|
-
raise(
|
|
381
|
-
ArgumentError,
|
|
382
|
-
"Invalid argument to :verify_host_key (or deprecated " \
|
|
383
|
-
":paranoid): #{verifier.inspect}"
|
|
384
|
-
)
|
|
354
|
+
raise ArgumentError, "Invalid argument to :verify_host_key: #{verifier.inspect}" unless verifier.respond_to?(:verify)
|
|
355
|
+
|
|
356
|
+
unless verifier.respond_to?(:verify_signature)
|
|
357
|
+
raise ArgumentError,
|
|
358
|
+
"verify_host_key verifier #{verifier.inspect} must implement verify_signature(&block): " \
|
|
359
|
+
"yield to the block, which checks the server's host key signature, and return its result"
|
|
385
360
|
end
|
|
361
|
+
|
|
362
|
+
verifier
|
|
386
363
|
end
|
|
387
364
|
end
|
|
388
365
|
end
|
|
@@ -51,9 +51,6 @@ module Net
|
|
|
51
51
|
# implement them (Net::SSH::HostKeyEntries::CertAuthority). Non-certificate
|
|
52
52
|
# entries do not respond to these predicates and are left untouched.
|
|
53
53
|
def verify_certificate!(entry, host_keys, arguments)
|
|
54
|
-
# Plain keys carry no certificate constraints, and respond_to? on this delegator prints a deprecation warning (#1026).
|
|
55
|
-
return if entry.is_a?(Net::SSH::HostKeyEntries::PubKey)
|
|
56
|
-
|
|
57
54
|
cert = arguments[:key]
|
|
58
55
|
|
|
59
56
|
if entry.respond_to?(:matches_validity?) && !entry.matches_validity?(cert)
|
data/lib/net/ssh/version.rb
CHANGED