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
|
@@ -3,8 +3,6 @@ gem 'bcrypt_pbkdf', '~> 1.0' unless RUBY_PLATFORM == "java"
|
|
|
3
3
|
|
|
4
4
|
require 'ed25519'
|
|
5
5
|
|
|
6
|
-
require 'base64'
|
|
7
|
-
|
|
8
6
|
require 'net/ssh/transport/cipher_factory'
|
|
9
7
|
require 'net/ssh/authentication/pub_key_fingerprint'
|
|
10
8
|
require 'bcrypt_pbkdf' unless RUBY_PLATFORM == "java"
|
|
@@ -14,7 +12,7 @@ module Net
|
|
|
14
12
|
module Authentication
|
|
15
13
|
module ED25519
|
|
16
14
|
class SigningKeyFromFile < SimpleDelegator
|
|
17
|
-
def initialize(pk,sk)
|
|
15
|
+
def initialize(pk, sk)
|
|
18
16
|
key = ::Ed25519::SigningKey.from_keypair(sk)
|
|
19
17
|
raise ArgumentError, "pk does not match sk" unless pk == key.verify_key.to_bytes
|
|
20
18
|
|
|
@@ -44,9 +42,11 @@ module Net
|
|
|
44
42
|
datafull = datafull.strip
|
|
45
43
|
raise ArgumentError.new("Expected #{MBEGIN} at start of private key") unless datafull.start_with?(MBEGIN)
|
|
46
44
|
raise ArgumentError.new("Expected #{MEND} at end of private key") unless datafull.end_with?(MEND)
|
|
45
|
+
|
|
47
46
|
datab64 = datafull[MBEGIN.size...-MEND.size]
|
|
48
|
-
data =
|
|
47
|
+
data = datab64.unpack1("m")
|
|
49
48
|
raise ArgumentError.new("Expected #{MAGIC} at start of decoded private key") unless data.start_with?(MAGIC)
|
|
49
|
+
|
|
50
50
|
buffer = Net::SSH::Buffer.new(data[MAGIC.size + 1..-1])
|
|
51
51
|
|
|
52
52
|
ciphername = buffer.read_string
|
|
@@ -59,6 +59,7 @@ module Net
|
|
|
59
59
|
kdfopts = Net::SSH::Buffer.new(buffer.read_string)
|
|
60
60
|
num_keys = buffer.read_long
|
|
61
61
|
raise ArgumentError.new("Only 1 key is supported in ssh keys #{num_keys} was in private key") unless num_keys == 1
|
|
62
|
+
|
|
62
63
|
_pubkey = buffer.read_string
|
|
63
64
|
|
|
64
65
|
len = buffer.read_long
|
|
@@ -72,12 +73,14 @@ module Net
|
|
|
72
73
|
rounds = kdfopts.read_long
|
|
73
74
|
|
|
74
75
|
raise "BCryptPbkdf is not implemented for jruby" if RUBY_PLATFORM == "java"
|
|
76
|
+
|
|
75
77
|
key = BCryptPbkdf::key(password, salt, keylen + ivlen, rounds)
|
|
78
|
+
raise DecryptError.new("BCyryptPbkdf failed", encrypted_key: true) unless key
|
|
76
79
|
else
|
|
77
80
|
key = '\x00' * (keylen + ivlen)
|
|
78
81
|
end
|
|
79
82
|
|
|
80
|
-
cipher = CipherFactory.get(ciphername, key: key[0...keylen], iv:key[keylen...keylen + ivlen], decrypt: true)
|
|
83
|
+
cipher = CipherFactory.get(ciphername, key: key[0...keylen], iv: key[keylen...keylen + ivlen], decrypt: true)
|
|
81
84
|
|
|
82
85
|
decoded = cipher.update(buffer.remainder_as_buffer.to_s)
|
|
83
86
|
decoded << cipher.final
|
|
@@ -112,7 +115,7 @@ module Net
|
|
|
112
115
|
end
|
|
113
116
|
|
|
114
117
|
def to_blob
|
|
115
|
-
Net::SSH::Buffer.from(:mstring,"ssh-ed25519"
|
|
118
|
+
Net::SSH::Buffer.from(:mstring, "ssh-ed25519".dup, :string, @verify_key.to_bytes).to_s
|
|
116
119
|
end
|
|
117
120
|
|
|
118
121
|
def ssh_type
|
|
@@ -123,13 +126,13 @@ module Net
|
|
|
123
126
|
ssh_type
|
|
124
127
|
end
|
|
125
128
|
|
|
126
|
-
def ssh_do_verify(sig,data)
|
|
127
|
-
@verify_key.verify(sig,data)
|
|
129
|
+
def ssh_do_verify(sig, data, options = {})
|
|
130
|
+
@verify_key.verify(sig, data)
|
|
128
131
|
end
|
|
129
132
|
|
|
130
133
|
def to_pem
|
|
131
134
|
# TODO this is not pem
|
|
132
|
-
ssh_type +
|
|
135
|
+
ssh_type + [@verify_key.to_bytes].pack("m")
|
|
133
136
|
end
|
|
134
137
|
end
|
|
135
138
|
|
|
@@ -148,7 +151,7 @@ module Net
|
|
|
148
151
|
_comment = buffer.read_string
|
|
149
152
|
|
|
150
153
|
@pk = pk
|
|
151
|
-
@sign_key = SigningKeyFromFile.new(pk,sk)
|
|
154
|
+
@sign_key = SigningKeyFromFile.new(pk, sk)
|
|
152
155
|
end
|
|
153
156
|
|
|
154
157
|
def to_blob
|
|
@@ -167,7 +170,7 @@ module Net
|
|
|
167
170
|
PubKey.new(@pk)
|
|
168
171
|
end
|
|
169
172
|
|
|
170
|
-
def ssh_do_sign(data)
|
|
173
|
+
def ssh_do_sign(data, sig_alg = nil)
|
|
171
174
|
@sign_key.sign(data)
|
|
172
175
|
end
|
|
173
176
|
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
module Net
|
|
2
|
-
module SSH
|
|
1
|
+
module Net
|
|
2
|
+
module SSH
|
|
3
3
|
module Authentication
|
|
4
|
-
|
|
5
4
|
# Loads ED25519 support which requires optinal dependecies like
|
|
6
5
|
# ed25519, bcrypt_pbkdf
|
|
7
6
|
module ED25519Loader
|
|
8
|
-
|
|
9
7
|
begin
|
|
10
8
|
require 'net/ssh/authentication/ed25519'
|
|
11
9
|
LOADED = true
|
|
@@ -14,20 +12,19 @@ module Net
|
|
|
14
12
|
ERROR = e
|
|
15
13
|
LOADED = false
|
|
16
14
|
end
|
|
17
|
-
|
|
15
|
+
|
|
18
16
|
def self.raiseUnlessLoaded(message)
|
|
19
17
|
description = ERROR.is_a?(LoadError) ? dependenciesRequiredForED25519 : ''
|
|
20
18
|
description << "#{ERROR.class} : \"#{ERROR.message}\"\n" if ERROR
|
|
21
19
|
raise NotImplementedError, "#{message}\n#{description}" unless LOADED
|
|
22
20
|
end
|
|
23
|
-
|
|
21
|
+
|
|
24
22
|
def self.dependenciesRequiredForED25519
|
|
25
23
|
result = "net-ssh requires the following gems for ed25519 support:\n"
|
|
26
24
|
result << " * ed25519 (>= 1.2, < 2.0)\n"
|
|
27
25
|
result << " * bcrypt_pbkdf (>= 1.0, < 2.0)\n" unless RUBY_PLATFORM == "java"
|
|
28
26
|
result << "See https://github.com/net-ssh/net-ssh/issues/565 for more information\n"
|
|
29
27
|
end
|
|
30
|
-
|
|
31
28
|
end
|
|
32
29
|
end
|
|
33
30
|
end
|
|
@@ -6,7 +6,6 @@ require 'net/ssh/authentication/agent'
|
|
|
6
6
|
module Net
|
|
7
7
|
module SSH
|
|
8
8
|
module Authentication
|
|
9
|
-
|
|
10
9
|
# A trivial exception class used to report errors in the key manager.
|
|
11
10
|
class KeyManagerError < Net::SSH::Exception; end
|
|
12
11
|
|
|
@@ -33,6 +32,9 @@ module Net
|
|
|
33
32
|
# The list of user key certificate files that will be examined
|
|
34
33
|
attr_reader :keycert_files
|
|
35
34
|
|
|
35
|
+
# The list of user key certificate data that will be examined
|
|
36
|
+
attr_reader :keycert_data
|
|
37
|
+
|
|
36
38
|
# The map of loaded identities
|
|
37
39
|
attr_reader :known_identities
|
|
38
40
|
|
|
@@ -42,11 +44,12 @@ module Net
|
|
|
42
44
|
# Create a new KeyManager. By default, the manager will
|
|
43
45
|
# use the ssh-agent if it is running and the `:use_agent` option
|
|
44
46
|
# is not false.
|
|
45
|
-
def initialize(logger, options={})
|
|
47
|
+
def initialize(logger, options = {})
|
|
46
48
|
self.logger = logger
|
|
47
49
|
@key_files = []
|
|
48
50
|
@key_data = []
|
|
49
51
|
@keycert_files = []
|
|
52
|
+
@keycert_data = []
|
|
50
53
|
@use_agent = options[:use_agent] != false
|
|
51
54
|
@known_identities = {}
|
|
52
55
|
@agent = nil
|
|
@@ -60,6 +63,7 @@ module Net
|
|
|
60
63
|
def clear!
|
|
61
64
|
key_files.clear
|
|
62
65
|
key_data.clear
|
|
66
|
+
keycert_data.clear
|
|
63
67
|
known_identities.clear
|
|
64
68
|
self
|
|
65
69
|
end
|
|
@@ -76,6 +80,12 @@ module Net
|
|
|
76
80
|
self
|
|
77
81
|
end
|
|
78
82
|
|
|
83
|
+
# Add the given keycert_data to the list of keycerts that will be used.
|
|
84
|
+
def add_keycert_data(keycert_data_)
|
|
85
|
+
keycert_data.push(keycert_data_).uniq!
|
|
86
|
+
self
|
|
87
|
+
end
|
|
88
|
+
|
|
79
89
|
# Add the given key_file to the list of keys that will be used.
|
|
80
90
|
def add_key_data(key_data_)
|
|
81
91
|
key_data.push(key_data_).uniq!
|
|
@@ -133,8 +143,8 @@ module Net
|
|
|
133
143
|
end
|
|
134
144
|
|
|
135
145
|
known_identity_blobs = known_identities.keys.map(&:to_blob)
|
|
136
|
-
|
|
137
|
-
|
|
146
|
+
|
|
147
|
+
keycerts.each do |keycert|
|
|
138
148
|
next if known_identity_blobs.include?(keycert.to_blob)
|
|
139
149
|
|
|
140
150
|
(_, corresponding_identity) = known_identities.detect { |public_key, _|
|
|
@@ -159,7 +169,7 @@ module Net
|
|
|
159
169
|
# Regardless of the identity's origin or who does the signing, this
|
|
160
170
|
# will always return the signature in an SSH2-specified "signature
|
|
161
171
|
# blob" format.
|
|
162
|
-
def sign(identity, data)
|
|
172
|
+
def sign(identity, data, sig_alg = nil)
|
|
163
173
|
info = known_identities[identity] or raise KeyManagerError, "the given identity is unknown to the key manager"
|
|
164
174
|
|
|
165
175
|
if info[:key].nil? && info[:from] == :file
|
|
@@ -171,13 +181,27 @@ module Net
|
|
|
171
181
|
end
|
|
172
182
|
|
|
173
183
|
if info[:key]
|
|
174
|
-
|
|
175
|
-
|
|
184
|
+
if sig_alg.nil?
|
|
185
|
+
signed = info[:key].ssh_do_sign(data.to_s)
|
|
186
|
+
sig_alg = identity.ssh_signature_type
|
|
187
|
+
else
|
|
188
|
+
signed = info[:key].ssh_do_sign(data.to_s, sig_alg)
|
|
189
|
+
end
|
|
190
|
+
return Net::SSH::Buffer.from(:string, sig_alg,
|
|
191
|
+
:mstring, signed).to_s
|
|
176
192
|
end
|
|
177
193
|
|
|
178
194
|
if info[:from] == :agent
|
|
179
195
|
raise KeyManagerError, "the agent is no longer available" unless agent
|
|
180
|
-
|
|
196
|
+
|
|
197
|
+
case sig_alg
|
|
198
|
+
when "rsa-sha2-512"
|
|
199
|
+
return agent.sign(info[:identity], data.to_s, Net::SSH::Authentication::Agent::SSH_AGENT_RSA_SHA2_512)
|
|
200
|
+
when "rsa-sha2-256"
|
|
201
|
+
return agent.sign(info[:identity], data.to_s, Net::SSH::Authentication::Agent::SSH_AGENT_RSA_SHA2_256)
|
|
202
|
+
else
|
|
203
|
+
return agent.sign(info[:identity], data.to_s)
|
|
204
|
+
end
|
|
181
205
|
end
|
|
182
206
|
|
|
183
207
|
raise KeyManagerError, "[BUG] can't determine identity origin (#{info.inspect})"
|
|
@@ -201,6 +225,7 @@ module Net
|
|
|
201
225
|
# or if the agent is otherwise not available.
|
|
202
226
|
def agent
|
|
203
227
|
return unless use_agent?
|
|
228
|
+
|
|
204
229
|
@agent ||= Agent.connect(logger, options[:agent_socket_factory], options[:identity_agent])
|
|
205
230
|
rescue AgentNotAvailable
|
|
206
231
|
@use_agent = false
|
|
@@ -213,6 +238,12 @@ module Net
|
|
|
213
238
|
|
|
214
239
|
private
|
|
215
240
|
|
|
241
|
+
# Load keycerts from files and data.
|
|
242
|
+
def keycerts
|
|
243
|
+
keycert_files.map { |keycert_file| KeyFactory.load_public_key(keycert_file) } +
|
|
244
|
+
keycert_data.map { |data| KeyFactory.load_data_public_key(data) }
|
|
245
|
+
end
|
|
246
|
+
|
|
216
247
|
# Prepares identities from user key_files for loading, preserving their order and sources.
|
|
217
248
|
def prepare_identities_from_files
|
|
218
249
|
key_files.map do |file|
|
|
@@ -248,37 +279,35 @@ module Net
|
|
|
248
279
|
# Load prepared identities. Private key decryption errors ignored if ignore_decryption_errors
|
|
249
280
|
def load_identities(identities, ask_passphrase, ignore_decryption_errors)
|
|
250
281
|
identities.map do |identity|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
else
|
|
275
|
-
process_identity_loading_error(identity, e)
|
|
276
|
-
nil
|
|
277
|
-
end
|
|
278
|
-
rescue Exception => e
|
|
282
|
+
case identity[:load_from]
|
|
283
|
+
when :pubkey_file
|
|
284
|
+
key = KeyFactory.load_public_key(identity[:pubkey_file])
|
|
285
|
+
{ public_key: key, from: :file, file: identity[:privkey_file] }
|
|
286
|
+
when :privkey_file
|
|
287
|
+
private_key = KeyFactory.load_private_key(
|
|
288
|
+
identity[:privkey_file], options[:passphrase], ask_passphrase, options[:password_prompt]
|
|
289
|
+
)
|
|
290
|
+
key = private_key.send(:public_key)
|
|
291
|
+
{ public_key: key, from: :file, file: identity[:privkey_file], key: private_key }
|
|
292
|
+
when :data
|
|
293
|
+
private_key = KeyFactory.load_data_private_key(
|
|
294
|
+
identity[:data], options[:passphrase], ask_passphrase, "<key in memory>", options[:password_prompt]
|
|
295
|
+
)
|
|
296
|
+
key = private_key.send(:public_key)
|
|
297
|
+
{ public_key: key, from: :key_data, data: identity[:data], key: private_key }
|
|
298
|
+
else
|
|
299
|
+
identity
|
|
300
|
+
end
|
|
301
|
+
rescue OpenSSL::PKey::RSAError, OpenSSL::PKey::DSAError, OpenSSL::PKey::ECError, OpenSSL::PKey::PKeyError, ArgumentError => e
|
|
302
|
+
if ignore_decryption_errors
|
|
303
|
+
identity
|
|
304
|
+
else
|
|
279
305
|
process_identity_loading_error(identity, e)
|
|
280
306
|
nil
|
|
281
307
|
end
|
|
308
|
+
rescue Exception => e
|
|
309
|
+
process_identity_loading_error(identity, e)
|
|
310
|
+
nil
|
|
282
311
|
end.compact
|
|
283
312
|
end
|
|
284
313
|
|
|
@@ -7,7 +7,6 @@ module Net
|
|
|
7
7
|
module SSH
|
|
8
8
|
module Authentication
|
|
9
9
|
module Methods
|
|
10
|
-
|
|
11
10
|
# The base class of all user authentication methods. It provides a few
|
|
12
11
|
# bits of common functionality.
|
|
13
12
|
class Abstract
|
|
@@ -21,12 +20,22 @@ module Net
|
|
|
21
20
|
# this.
|
|
22
21
|
attr_reader :key_manager
|
|
23
22
|
|
|
23
|
+
# So far only affects algorithms used for rsa keys, but can be
|
|
24
|
+
# extended to other keys, e.g after reading of
|
|
25
|
+
# PubkeyAcceptedAlgorithms option from ssh_config file is implemented.
|
|
26
|
+
attr_reader :pubkey_algorithms
|
|
27
|
+
|
|
24
28
|
# Instantiates a new authentication method.
|
|
25
|
-
def initialize(session, options={})
|
|
29
|
+
def initialize(session, options = {})
|
|
26
30
|
@session = session
|
|
27
31
|
@key_manager = options[:key_manager]
|
|
28
32
|
@options = options
|
|
29
33
|
@prompt = options[:password_prompt]
|
|
34
|
+
@pubkey_algorithms = options[:pubkey_algorithms] \
|
|
35
|
+
|| %w[rsa-sha2-256-cert-v01@openssh.com
|
|
36
|
+
ssh-rsa-cert-v01@openssh.com
|
|
37
|
+
rsa-sha2-256
|
|
38
|
+
ssh-rsa]
|
|
30
39
|
self.logger = session.logger
|
|
31
40
|
end
|
|
32
41
|
|
|
@@ -47,7 +56,7 @@ module Net
|
|
|
47
56
|
# of the packet. The new packet is returned, ready for sending.
|
|
48
57
|
def userauth_request(username, next_service, auth_method, *others)
|
|
49
58
|
buffer = Net::SSH::Buffer.from(:byte, USERAUTH_REQUEST,
|
|
50
|
-
|
|
59
|
+
:string, username, :string, next_service, :string, auth_method)
|
|
51
60
|
|
|
52
61
|
others.each do |value|
|
|
53
62
|
case value
|
|
@@ -4,19 +4,18 @@ module Net
|
|
|
4
4
|
module SSH
|
|
5
5
|
module Authentication
|
|
6
6
|
module Methods
|
|
7
|
-
|
|
8
7
|
# Implements the host-based SSH authentication method.
|
|
9
8
|
class Hostbased < Abstract
|
|
10
9
|
include Constants
|
|
11
10
|
|
|
12
11
|
# Attempts to perform host-based authorization of the user by trying
|
|
13
12
|
# all known keys.
|
|
14
|
-
def authenticate(next_service, username, password=nil)
|
|
13
|
+
def authenticate(next_service, username, password = nil)
|
|
15
14
|
return false unless key_manager
|
|
16
15
|
|
|
17
16
|
key_manager.each_identity do |identity|
|
|
18
17
|
return true if authenticate_with(identity, next_service,
|
|
19
|
-
|
|
18
|
+
username, key_manager)
|
|
20
19
|
end
|
|
21
20
|
|
|
22
21
|
return false
|
|
@@ -64,10 +63,9 @@ module Net
|
|
|
64
63
|
# Build the "core" hostbased request string.
|
|
65
64
|
def build_request(identity, next_service, username, hostname, client_username)
|
|
66
65
|
userauth_request(username, next_service, "hostbased", identity.ssh_type,
|
|
67
|
-
|
|
66
|
+
Buffer.from(:key, identity).to_s, hostname, client_username).to_s
|
|
68
67
|
end
|
|
69
68
|
end
|
|
70
|
-
|
|
71
69
|
end
|
|
72
70
|
end
|
|
73
71
|
end
|
|
@@ -5,14 +5,13 @@ module Net
|
|
|
5
5
|
module SSH
|
|
6
6
|
module Authentication
|
|
7
7
|
module Methods
|
|
8
|
-
|
|
9
8
|
# Implements the "keyboard-interactive" SSH authentication method.
|
|
10
9
|
class KeyboardInteractive < Abstract
|
|
11
10
|
USERAUTH_INFO_REQUEST = 60
|
|
12
11
|
USERAUTH_INFO_RESPONSE = 61
|
|
13
12
|
|
|
14
13
|
# Attempt to authenticate the given user for the given service.
|
|
15
|
-
def authenticate(next_service, username, password=nil)
|
|
14
|
+
def authenticate(next_service, username, password = nil)
|
|
16
15
|
debug { "trying keyboard-interactive" }
|
|
17
16
|
send_message(userauth_request(username, next_service, "keyboard-interactive", "", ""))
|
|
18
17
|
|
|
@@ -32,6 +31,7 @@ module Net
|
|
|
32
31
|
message[:authentications].split(/,/).include? 'keyboard-interactive'
|
|
33
32
|
|
|
34
33
|
return false unless interactive?
|
|
34
|
+
|
|
35
35
|
password = nil
|
|
36
36
|
debug { "retrying keyboard-interactive" }
|
|
37
37
|
send_message(userauth_request(username, next_service, "keyboard-interactive", "", ""))
|
|
@@ -5,32 +5,29 @@ module Net
|
|
|
5
5
|
module SSH
|
|
6
6
|
module Authentication
|
|
7
7
|
module Methods
|
|
8
|
-
|
|
9
8
|
# Implements the "none" SSH authentication method.
|
|
10
9
|
class None < Abstract
|
|
11
10
|
# Attempt to authenticate as "none"
|
|
12
|
-
def authenticate(next_service, user="", password="")
|
|
13
|
-
send_message(userauth_request(user, next_service, "none"))
|
|
11
|
+
def authenticate(next_service, user = "", password = "")
|
|
12
|
+
send_message(userauth_request(user, next_service, "none"))
|
|
14
13
|
message = session.next_message
|
|
15
|
-
|
|
14
|
+
|
|
16
15
|
case message.type
|
|
17
16
|
when USERAUTH_SUCCESS
|
|
18
17
|
debug { "none succeeded" }
|
|
19
18
|
return true
|
|
20
19
|
when USERAUTH_FAILURE
|
|
21
20
|
debug { "none failed" }
|
|
22
|
-
|
|
21
|
+
|
|
23
22
|
raise Net::SSH::Authentication::DisallowedMethod unless
|
|
24
23
|
message[:authentications].split(/,/).include? 'none'
|
|
25
|
-
|
|
24
|
+
|
|
26
25
|
return false
|
|
27
26
|
else
|
|
28
27
|
raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
|
|
29
|
-
end
|
|
30
|
-
|
|
28
|
+
end
|
|
31
29
|
end
|
|
32
30
|
end
|
|
33
|
-
|
|
34
31
|
end
|
|
35
32
|
end
|
|
36
33
|
end
|
|
@@ -6,12 +6,11 @@ module Net
|
|
|
6
6
|
module SSH
|
|
7
7
|
module Authentication
|
|
8
8
|
module Methods
|
|
9
|
-
|
|
10
9
|
# Implements the "password" SSH authentication method.
|
|
11
10
|
class Password < Abstract
|
|
12
11
|
# Attempt to authenticate the given user for the given service. If
|
|
13
12
|
# the password parameter is nil, this will ask for password
|
|
14
|
-
def authenticate(next_service, username, password=nil)
|
|
13
|
+
def authenticate(next_service, username, password = nil)
|
|
15
14
|
clear_prompter!
|
|
16
15
|
retries = 0
|
|
17
16
|
max_retries = get_max_retries
|
|
@@ -29,6 +28,7 @@ module Net
|
|
|
29
28
|
|
|
30
29
|
raise Net::SSH::Authentication::DisallowedMethod unless
|
|
31
30
|
message[:authentications].split(/,/).include? 'password'
|
|
31
|
+
|
|
32
32
|
password = nil
|
|
33
33
|
end
|
|
34
34
|
end until (message.type != USERAUTH_FAILURE || retries >= max_retries)
|
|
@@ -74,7 +74,6 @@ module Net
|
|
|
74
74
|
options[:non_interactive] ? 0 : result
|
|
75
75
|
end
|
|
76
76
|
end
|
|
77
|
-
|
|
78
77
|
end
|
|
79
78
|
end
|
|
80
79
|
end
|
|
@@ -6,14 +6,13 @@ module Net
|
|
|
6
6
|
module SSH
|
|
7
7
|
module Authentication
|
|
8
8
|
module Methods
|
|
9
|
-
|
|
10
9
|
# Implements the "publickey" SSH authentication method.
|
|
11
10
|
class Publickey < Abstract
|
|
12
11
|
# Attempts to perform public-key authentication for the given
|
|
13
12
|
# username, trying each identity known to the key manager. If any of
|
|
14
13
|
# them succeed, returns +true+, otherwise returns +false+. This
|
|
15
14
|
# requires the presence of a key manager.
|
|
16
|
-
def authenticate(next_service, username, password=nil)
|
|
15
|
+
def authenticate(next_service, username, password = nil)
|
|
17
16
|
return false unless key_manager
|
|
18
17
|
|
|
19
18
|
key_manager.each_identity do |identity|
|
|
@@ -27,41 +26,40 @@ module Net
|
|
|
27
26
|
|
|
28
27
|
# Builds a packet that contains the request formatted for sending
|
|
29
28
|
# a public-key request to the server.
|
|
30
|
-
def build_request(pub_key, username, next_service, has_sig)
|
|
29
|
+
def build_request(pub_key, username, next_service, alg, has_sig)
|
|
31
30
|
blob = Net::SSH::Buffer.new
|
|
32
31
|
blob.write_key pub_key
|
|
33
32
|
|
|
34
33
|
userauth_request(username, next_service, "publickey", has_sig,
|
|
35
|
-
|
|
34
|
+
alg, blob.to_s)
|
|
36
35
|
end
|
|
37
36
|
|
|
38
37
|
# Builds and sends a request formatted for a public-key
|
|
39
38
|
# authentication request.
|
|
40
|
-
def send_request(pub_key, username, next_service, signature=nil)
|
|
41
|
-
msg = build_request(pub_key, username, next_service,
|
|
39
|
+
def send_request(pub_key, username, next_service, alg, signature = nil)
|
|
40
|
+
msg = build_request(pub_key, username, next_service, alg,
|
|
41
|
+
!signature.nil?)
|
|
42
42
|
msg.write_string(signature) if signature
|
|
43
43
|
send_message(msg)
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def authenticate_with(identity, next_service, username)
|
|
50
|
-
debug { "trying publickey (#{identity.fingerprint})" }
|
|
51
|
-
send_request(identity, username, next_service)
|
|
46
|
+
def authenticate_with_alg(identity, next_service, username, alg, sig_alg = nil)
|
|
47
|
+
debug { "trying publickey (#{identity.fingerprint}) alg #{alg}" }
|
|
48
|
+
send_request(identity, username, next_service, alg)
|
|
52
49
|
|
|
53
50
|
message = session.next_message
|
|
54
51
|
|
|
55
52
|
case message.type
|
|
56
53
|
when USERAUTH_PK_OK
|
|
57
|
-
buffer = build_request(identity, username, next_service,
|
|
54
|
+
buffer = build_request(identity, username, next_service, alg,
|
|
55
|
+
true)
|
|
58
56
|
sig_data = Net::SSH::Buffer.new
|
|
59
57
|
sig_data.write_string(session_id)
|
|
60
58
|
sig_data.append(buffer.to_s)
|
|
61
59
|
|
|
62
|
-
sig_blob = key_manager.sign(identity, sig_data)
|
|
60
|
+
sig_blob = key_manager.sign(identity, sig_data, sig_alg)
|
|
63
61
|
|
|
64
|
-
send_request(identity, username, next_service, sig_blob.to_s)
|
|
62
|
+
send_request(identity, username, next_service, alg, sig_blob.to_s)
|
|
65
63
|
message = session.next_message
|
|
66
64
|
|
|
67
65
|
case message.type
|
|
@@ -77,7 +75,7 @@ module Net
|
|
|
77
75
|
return false
|
|
78
76
|
else
|
|
79
77
|
raise Net::SSH::Exception,
|
|
80
|
-
|
|
78
|
+
"unexpected server response to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
|
|
81
79
|
end
|
|
82
80
|
|
|
83
81
|
when USERAUTH_FAILURE
|
|
@@ -89,8 +87,50 @@ module Net
|
|
|
89
87
|
raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
|
|
90
88
|
end
|
|
91
89
|
end
|
|
92
|
-
end
|
|
93
90
|
|
|
91
|
+
# Attempts to perform public-key authentication for the given
|
|
92
|
+
# username, with the given identity (public key). Returns +true+ if
|
|
93
|
+
# successful, or +false+ otherwise.
|
|
94
|
+
def authenticate_with(identity, next_service, username)
|
|
95
|
+
type = identity.ssh_type
|
|
96
|
+
if type == "ssh-rsa"
|
|
97
|
+
pubkey_algorithms.each do |pk_alg|
|
|
98
|
+
case pk_alg
|
|
99
|
+
when "rsa-sha2-512", "rsa-sha2-256", "ssh-rsa"
|
|
100
|
+
if authenticate_with_alg(identity, next_service, username, pk_alg, pk_alg)
|
|
101
|
+
# success
|
|
102
|
+
return true
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
elsif type == "ssh-rsa-cert-v01@openssh.com"
|
|
107
|
+
pubkey_algorithms.each do |pk_alg|
|
|
108
|
+
case pk_alg
|
|
109
|
+
when "rsa-sha2-512-cert-v01@openssh.com"
|
|
110
|
+
if authenticate_with_alg(identity, next_service, username, pk_alg, "rsa-sha2-512")
|
|
111
|
+
# success
|
|
112
|
+
return true
|
|
113
|
+
end
|
|
114
|
+
when "rsa-sha2-256-cert-v01@openssh.com"
|
|
115
|
+
if authenticate_with_alg(identity, next_service, username, pk_alg, "rsa-sha2-256")
|
|
116
|
+
# success
|
|
117
|
+
return true
|
|
118
|
+
end
|
|
119
|
+
when "ssh-rsa-cert-v01@openssh.com"
|
|
120
|
+
if authenticate_with_alg(identity, next_service, username, pk_alg)
|
|
121
|
+
# success
|
|
122
|
+
return true
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
elsif authenticate_with_alg(identity, next_service, username, type)
|
|
127
|
+
# success
|
|
128
|
+
return true
|
|
129
|
+
end
|
|
130
|
+
# failure
|
|
131
|
+
return false
|
|
132
|
+
end
|
|
133
|
+
end
|
|
94
134
|
end
|
|
95
135
|
end
|
|
96
136
|
end
|