net-ssh 7.3.6 → 8.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/.github/pull_request_template.md +1 -0
- data/.github/workflows/changelog.yml +43 -0
- data/.github/workflows/ci-with-docker.yml +0 -3
- data/.github/workflows/ci.yml +2 -17
- data/.github/workflows/rubocop.yml +3 -1
- data/.rubocop.yml +1 -1
- data/.rubocop_todo.yml +1 -0
- data/CHANGES.txt +73 -7
- data/CONTRIBUTING.md +35 -0
- data/Dockerfile +3 -3
- data/Dockerfile.openssl3 +1 -1
- data/Dockerfile.zlib_ng +1 -1
- data/README.md +71 -57
- data/Rakefile +66 -12
- data/appveyor.yml +4 -16
- data/changelog.d/README.md +1 -0
- data/docker-compose.yml +1 -7
- data/lib/net/ssh/authentication/agent.rb +35 -11
- data/lib/net/ssh/authentication/ed25519.rb +137 -44
- data/lib/net/ssh/authentication/ed25519_loader.rb +14 -9
- data/lib/net/ssh/authentication/key_manager.rb +22 -11
- 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 +28 -12
- 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/prompt.rb +3 -3
- data/lib/net/ssh/proxy/jump.rb +40 -2
- data/lib/net/ssh/service/forward.rb +29 -24
- data/lib/net/ssh/test.rb +2 -1
- data/lib/net/ssh/transport/algorithms.rb +88 -33
- data/lib/net/ssh/transport/chacha20_poly1305_cipher.rb +89 -16
- data/lib/net/ssh/transport/chacha20_poly1305_cipher_loader.rb +20 -1
- data/lib/net/ssh/transport/cipher_factory.rb +23 -1
- data/lib/net/ssh/transport/constants.rb +1 -0
- data/lib/net/ssh/transport/gcm_cipher.rb +27 -6
- 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/curve25519_sha256.rb +41 -9
- data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +14 -6
- 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/packet_stream.rb +12 -0
- data/lib/net/ssh/transport/session.rb +41 -44
- data/lib/net/ssh/verifiers/always.rb +0 -3
- data/lib/net/ssh/version.rb +3 -3
- data/lib/net/ssh.rb +139 -57
- data/net-ssh.gemspec +5 -8
- data/rakelib/changelog.rb +229 -0
- data.tar.gz.sig +0 -0
- metadata +37 -36
- metadata.gz.sig +0 -0
- data/Gemfile.noed25519 +0 -12
- data/Gemfile.norbnacl +0 -12
|
@@ -5,7 +5,11 @@ module Net
|
|
|
5
5
|
module Transport
|
|
6
6
|
## Extension module for aes(128|256)gcm ciphers
|
|
7
7
|
module GCMCipher
|
|
8
|
-
|
|
8
|
+
BLOCK_SIZE = 16
|
|
9
|
+
IV_BYTES = 12
|
|
10
|
+
AUTH_TAG_BYTES = 16
|
|
11
|
+
|
|
12
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
9
13
|
def self.extended(orig)
|
|
10
14
|
# rubocop:disable Metrics/BlockLength
|
|
11
15
|
orig.class_eval do
|
|
@@ -100,15 +104,32 @@ module Net
|
|
|
100
104
|
end
|
|
101
105
|
|
|
102
106
|
def mac_length
|
|
103
|
-
|
|
107
|
+
GCMCipher::AUTH_TAG_BYTES
|
|
104
108
|
end
|
|
105
109
|
|
|
106
110
|
def block_size
|
|
107
|
-
|
|
111
|
+
GCMCipher::BLOCK_SIZE
|
|
108
112
|
end
|
|
109
113
|
|
|
110
114
|
def self.block_size
|
|
111
|
-
|
|
115
|
+
GCMCipher::BLOCK_SIZE
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def self.iv_len
|
|
119
|
+
GCMCipher::IV_BYTES
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def self.auth_length
|
|
123
|
+
GCMCipher::AUTH_TAG_BYTES
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def self.decrypt_private_key(ciphertext, auth_tag, key, initialization_vector)
|
|
127
|
+
cipher = new(encrypt: false, key: key)
|
|
128
|
+
|
|
129
|
+
cipher.nonce = initialization_vector
|
|
130
|
+
cipher.cipher.auth_tag = auth_tag.to_s
|
|
131
|
+
cipher.cipher.auth_data = ''
|
|
132
|
+
cipher.cipher.update(ciphertext.to_s) << cipher.cipher.final
|
|
112
133
|
end
|
|
113
134
|
|
|
114
135
|
#
|
|
@@ -117,7 +138,7 @@ module Net
|
|
|
117
138
|
# N_MAX maximum nonce (IV) length 12 octets
|
|
118
139
|
#
|
|
119
140
|
def iv_len
|
|
120
|
-
|
|
141
|
+
GCMCipher::IV_BYTES
|
|
121
142
|
end
|
|
122
143
|
|
|
123
144
|
#
|
|
@@ -201,7 +222,7 @@ module Net
|
|
|
201
222
|
end
|
|
202
223
|
# rubocop:enable Metrics/BlockLength
|
|
203
224
|
end
|
|
204
|
-
# rubocop:enable Metrics/AbcSize
|
|
225
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
|
205
226
|
end
|
|
206
227
|
end
|
|
207
228
|
end
|
|
@@ -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
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
require 'x25519'
|
|
4
|
-
|
|
1
|
+
require 'openssl'
|
|
5
2
|
require 'net/ssh/transport/constants'
|
|
6
3
|
require 'net/ssh/transport/kex/abstract5656'
|
|
7
4
|
|
|
@@ -12,25 +9,60 @@ module Net
|
|
|
12
9
|
# A key-exchange service implementing the "curve25519-sha256@libssh.org"
|
|
13
10
|
# key-exchange algorithm. (defined in https://tools.ietf.org/html/draft-ietf-curdle-ssh-curves-06)
|
|
14
11
|
class Curve25519Sha256 < Abstract5656
|
|
12
|
+
ALGORITHM = "X25519"
|
|
13
|
+
KEY_BYTES = 32
|
|
14
|
+
|
|
15
|
+
class UnsupportedError < StandardError; end
|
|
16
|
+
|
|
17
|
+
def self.ensure_supported!
|
|
18
|
+
unless OpenSSL::PKey.respond_to?(:generate_key) && OpenSSL::PKey.respond_to?(:new_raw_public_key)
|
|
19
|
+
raise UnsupportedError, "OpenSSL::PKey raw public key APIs are unavailable"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
key = generate_x25519_key
|
|
23
|
+
public_key = new_raw_public_key(key.raw_public_key)
|
|
24
|
+
validate_key_bytes!(key.derive(public_key), "shared secret", KEY_BYTES)
|
|
25
|
+
rescue OpenSSL::PKey::PKeyError => e
|
|
26
|
+
raise UnsupportedError, e.message
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.generate_x25519_key
|
|
30
|
+
OpenSSL::PKey.generate_key(ALGORITHM)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.new_raw_public_key(key)
|
|
34
|
+
validate_key_bytes!(key, "public key", KEY_BYTES)
|
|
35
|
+
OpenSSL::PKey.new_raw_public_key(ALGORITHM, binary_string(key))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.validate_key_bytes!(key, label, expected_bytes)
|
|
39
|
+
raise ArgumentError, "invalid X25519 #{label}" unless key.respond_to?(:bytesize) && key.bytesize == expected_bytes
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.binary_string(string)
|
|
43
|
+
string.dup.force_encoding('BINARY')
|
|
44
|
+
end
|
|
45
|
+
private_class_method :binary_string
|
|
46
|
+
|
|
15
47
|
def digester
|
|
16
48
|
OpenSSL::Digest::SHA256
|
|
17
49
|
end
|
|
18
50
|
|
|
19
51
|
private
|
|
20
52
|
|
|
21
|
-
def generate_key
|
|
22
|
-
|
|
53
|
+
def generate_key
|
|
54
|
+
self.class.generate_x25519_key
|
|
23
55
|
end
|
|
24
56
|
|
|
25
57
|
## string Q_C, client's ephemeral public key octet string
|
|
26
58
|
def ecdh_public_key_bytes
|
|
27
|
-
ecdh.
|
|
59
|
+
ecdh.raw_public_key
|
|
28
60
|
end
|
|
29
61
|
|
|
30
62
|
# compute shared secret from server's public key and client's private key
|
|
31
63
|
def compute_shared_secret(server_ecdh_pubkey)
|
|
32
|
-
pk =
|
|
33
|
-
OpenSSL::BN.new(ecdh.
|
|
64
|
+
pk = self.class.new_raw_public_key(server_ecdh_pubkey)
|
|
65
|
+
OpenSSL::BN.new(ecdh.derive(pk), 2)
|
|
34
66
|
end
|
|
35
67
|
end
|
|
36
68
|
end
|
|
@@ -2,26 +2,34 @@ module Net
|
|
|
2
2
|
module SSH
|
|
3
3
|
module Transport
|
|
4
4
|
module Kex
|
|
5
|
-
# Loads Curve25519Sha256 support
|
|
5
|
+
# Loads Curve25519Sha256 support backed by Ruby OpenSSL X25519 APIs.
|
|
6
6
|
module Curve25519Sha256Loader
|
|
7
7
|
begin
|
|
8
8
|
require 'net/ssh/transport/kex/curve25519_sha256'
|
|
9
|
+
Net::SSH::Transport::Kex::Curve25519Sha256.ensure_supported!
|
|
9
10
|
LOADED = true
|
|
10
11
|
ERROR = nil
|
|
11
12
|
rescue LoadError => e
|
|
12
13
|
ERROR = e
|
|
13
14
|
LOADED = false
|
|
15
|
+
rescue StandardError => e
|
|
16
|
+
if defined?(Net::SSH::Transport::Kex::Curve25519Sha256::UnsupportedError) &&
|
|
17
|
+
e.is_a?(Net::SSH::Transport::Kex::Curve25519Sha256::UnsupportedError)
|
|
18
|
+
ERROR = e
|
|
19
|
+
LOADED = false
|
|
20
|
+
else
|
|
21
|
+
raise
|
|
22
|
+
end
|
|
14
23
|
end
|
|
15
24
|
|
|
16
25
|
def self.raiseUnlessLoaded(message)
|
|
17
|
-
description =
|
|
18
|
-
description
|
|
26
|
+
description = LOADED ? '' : x25519SupportRequired
|
|
27
|
+
description += "#{ERROR.class} : \"#{ERROR.message}\"\n" if ERROR
|
|
19
28
|
raise NotImplementedError, "#{message}\n#{description}" unless LOADED
|
|
20
29
|
end
|
|
21
30
|
|
|
22
|
-
def self.
|
|
23
|
-
|
|
24
|
-
result << " * x25519\n"
|
|
31
|
+
def self.x25519SupportRequired
|
|
32
|
+
"net-ssh requires openssl >= 3.2.0 and a crypto backend with X25519 enabled for curve25519-sha256 support.\n"
|
|
25
33
|
end
|
|
26
34
|
end
|
|
27
35
|
end
|
|
@@ -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
|
|
|
@@ -190,6 +190,7 @@ module Net
|
|
|
190
190
|
def cleanup
|
|
191
191
|
client.cleanup
|
|
192
192
|
server.cleanup
|
|
193
|
+
hang_up_proxy_command
|
|
193
194
|
end
|
|
194
195
|
|
|
195
196
|
# If the IO object requires a rekey operation (as indicated by either its
|
|
@@ -205,6 +206,17 @@ module Net
|
|
|
205
206
|
|
|
206
207
|
protected
|
|
207
208
|
|
|
209
|
+
# Like OpenSSH, hang up a ProxyCommand child: tunnels such as EC2 Instance
|
|
210
|
+
# Connect don't exit on their own, and IO#close waits for them.
|
|
211
|
+
def hang_up_proxy_command
|
|
212
|
+
return unless respond_to?(:pid)
|
|
213
|
+
|
|
214
|
+
child = pid
|
|
215
|
+
Process.kill('HUP', child) if child
|
|
216
|
+
rescue IOError, Errno::ESRCH
|
|
217
|
+
# already closed, or the child has already been reaped
|
|
218
|
+
end
|
|
219
|
+
|
|
208
220
|
# Called when this module is used to extend an object. It initializes
|
|
209
221
|
# the states and generally prepares the object for use as a packet stream.
|
|
210
222
|
def initialize_ssh
|
|
@@ -66,6 +66,7 @@ module Net
|
|
|
66
66
|
@port = options[:port] || DEFAULT_PORT
|
|
67
67
|
@bind_address = options[:bind_address] || nil
|
|
68
68
|
@options = options
|
|
69
|
+
@host_key_verifier = select_host_key_verifier(options[:verify_host_key])
|
|
69
70
|
|
|
70
71
|
@socket =
|
|
71
72
|
if (factory = options[:proxy])
|
|
@@ -79,21 +80,24 @@ module Net
|
|
|
79
80
|
|
|
80
81
|
@socket.extend(PacketStream)
|
|
81
82
|
@socket.logger = @logger
|
|
83
|
+
set_no_delay unless options[:no_delay] == false
|
|
82
84
|
|
|
83
85
|
debug { "connection established" }
|
|
84
86
|
|
|
85
87
|
@queue = []
|
|
86
88
|
@queued_bytes = 0
|
|
87
89
|
|
|
88
|
-
@host_key_verifier = select_host_key_verifier(options[:verify_host_key])
|
|
89
|
-
|
|
90
90
|
@server_version = ServerVersion.new(socket, logger, options[:timeout])
|
|
91
91
|
|
|
92
92
|
@algorithms = Algorithms.new(self, options)
|
|
93
93
|
@algorithms.start
|
|
94
94
|
wait { algorithms.initialized? }
|
|
95
95
|
rescue Errno::ETIMEDOUT
|
|
96
|
+
@socket&.close
|
|
96
97
|
raise Net::SSH::ConnectionTimeout
|
|
98
|
+
rescue StandardError
|
|
99
|
+
@socket&.close
|
|
100
|
+
raise
|
|
97
101
|
end
|
|
98
102
|
|
|
99
103
|
def host_keys
|
|
@@ -103,6 +107,10 @@ module Net
|
|
|
103
107
|
end
|
|
104
108
|
end
|
|
105
109
|
|
|
110
|
+
def pubkey_algorithms
|
|
111
|
+
algorithms[:pubkey_algorithms]
|
|
112
|
+
end
|
|
113
|
+
|
|
106
114
|
# Returns the host (and possibly IP address) in a format compatible with
|
|
107
115
|
# SSH known-host files.
|
|
108
116
|
def host_as_string
|
|
@@ -211,6 +219,10 @@ module Net
|
|
|
211
219
|
when DEBUG
|
|
212
220
|
send(packet[:always_display] ? :fatal : :debug) { packet[:message] }
|
|
213
221
|
|
|
222
|
+
# We don't advertise ext-info-c, but some servers send EXT_INFO anyway (#955).
|
|
223
|
+
when EXT_INFO
|
|
224
|
+
debug { "ignoring EXT_INFO packet" }
|
|
225
|
+
|
|
214
226
|
when KEXINIT
|
|
215
227
|
algorithms.accept_kexinit(packet)
|
|
216
228
|
|
|
@@ -293,23 +305,25 @@ module Net
|
|
|
293
305
|
packet
|
|
294
306
|
end
|
|
295
307
|
|
|
296
|
-
#
|
|
297
|
-
#
|
|
298
|
-
#
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
308
|
+
# Turns Nagle's algorithm off on the socket, so that a packet written
|
|
309
|
+
# while the previous one is still unacknowledged goes out right away
|
|
310
|
+
# instead of waiting for the server's ACK. A ProxyCommand hands the
|
|
311
|
+
# transport a pipe rather than a socket, and the command on its other
|
|
312
|
+
# end owns the TCP connection, so there is nothing to set in that case.
|
|
313
|
+
def set_no_delay
|
|
314
|
+
return unless socket.respond_to?(:setsockopt)
|
|
303
315
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
end
|
|
307
|
-
|
|
308
|
-
def verify_signature(&block)
|
|
309
|
-
yield
|
|
310
|
-
end
|
|
316
|
+
debug { "setting TCP_NODELAY" }
|
|
317
|
+
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
|
311
318
|
end
|
|
312
319
|
|
|
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
|
|
326
|
+
|
|
313
327
|
# Instantiates a new host-key verification class, based on the value of
|
|
314
328
|
# the parameter.
|
|
315
329
|
#
|
|
@@ -323,46 +337,29 @@ module Net
|
|
|
323
337
|
#
|
|
324
338
|
# If the argument happens to respond to :verify and :verify_signature,
|
|
325
339
|
# it is returned directly. Otherwise, an exception is raised.
|
|
326
|
-
#
|
|
327
|
-
# Values false, true, and :very were deprecated in
|
|
328
|
-
# [#595](https://github.com/net-ssh/net-ssh/pull/595)
|
|
329
340
|
def select_host_key_verifier(verifier)
|
|
330
341
|
case verifier
|
|
331
|
-
when false
|
|
332
|
-
Kernel.warn('verify_host_key: false is deprecated, use :never')
|
|
333
|
-
Net::SSH::Verifiers::Never.new
|
|
334
342
|
when :never then
|
|
335
343
|
Net::SSH::Verifiers::Never.new
|
|
336
|
-
when true
|
|
337
|
-
Kernel.warn('verify_host_key: true is deprecated, use :accept_new_or_local_tunnel')
|
|
338
|
-
Net::SSH::Verifiers::AcceptNewOrLocalTunnel.new
|
|
339
344
|
when :accept_new_or_local_tunnel, nil then
|
|
340
345
|
Net::SSH::Verifiers::AcceptNewOrLocalTunnel.new
|
|
341
|
-
when :very
|
|
342
|
-
Kernel.warn('verify_host_key: :very is deprecated, use :accept_new')
|
|
343
|
-
Net::SSH::Verifiers::AcceptNew.new
|
|
344
346
|
when :accept_new then
|
|
345
347
|
Net::SSH::Verifiers::AcceptNew.new
|
|
346
|
-
when :secure then
|
|
347
|
-
Kernel.warn('verify_host_key: :secure is deprecated, use :always')
|
|
348
|
-
Net::SSH::Verifiers::Always.new
|
|
349
348
|
when :always then
|
|
350
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}"
|
|
351
353
|
else
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
end
|
|
359
|
-
else
|
|
360
|
-
raise(
|
|
361
|
-
ArgumentError,
|
|
362
|
-
"Invalid argument to :verify_host_key (or deprecated " \
|
|
363
|
-
":paranoid): #{verifier.inspect}"
|
|
364
|
-
)
|
|
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"
|
|
365
360
|
end
|
|
361
|
+
|
|
362
|
+
verifier
|
|
366
363
|
end
|
|
367
364
|
end
|
|
368
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
|
@@ -46,13 +46,13 @@ module Net
|
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
# The major component of this version of the Net::SSH library
|
|
49
|
-
MAJOR =
|
|
49
|
+
MAJOR = 8
|
|
50
50
|
|
|
51
51
|
# The minor component of this version of the Net::SSH library
|
|
52
|
-
MINOR =
|
|
52
|
+
MINOR = 0
|
|
53
53
|
|
|
54
54
|
# The tiny component of this version of the Net::SSH library
|
|
55
|
-
TINY =
|
|
55
|
+
TINY = 0
|
|
56
56
|
|
|
57
57
|
# The prerelease component of this version of the Net::SSH library
|
|
58
58
|
# nil allowed
|