net-ssh 5.2.0 → 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 +5 -5
- 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 +16 -2
- data/.rubocop_todo.yml +623 -511
- data/CHANGES.txt +50 -2
- 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 +29 -13
- 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 +13 -8
- data/lib/net/ssh/authentication/ed25519_loader.rb +5 -8
- data/lib/net/ssh/authentication/key_manager.rb +73 -32
- 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 +56 -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 +51 -40
- data/lib/net/ssh/buffered_io.rb +24 -26
- data/lib/net/ssh/config.rb +82 -50
- 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 +10 -13
- data/lib/net/ssh/known_hosts.rb +106 -39
- 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 +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 +7 -7
- data/lib/net/ssh/transport/algorithms.rb +100 -58
- 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 -127
- data/lib/net/ssh/transport/packet_stream.rb +50 -16
- 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 +3 -3
- data/lib/net/ssh.rb +12 -8
- data/net-ssh-public_cert.pem +8 -8
- data/net-ssh.gemspec +9 -7
- data/support/ssh_tunnel_bug.rb +3 -3
- data.tar.gz.sig +0 -0
- metadata +55 -30
- metadata.gz.sig +0 -0
- data/.travis.yml +0 -53
- data/Gemfile.noed25519.lock +0 -41
- data/README.rdoc +0 -194
- data/lib/net/ssh/ruby_compat.rb +0 -13
- data/support/arcfour_check.rb +0 -20
|
@@ -11,7 +11,6 @@ require 'net/ssh/authentication/methods/keyboard_interactive'
|
|
|
11
11
|
module Net
|
|
12
12
|
module SSH
|
|
13
13
|
module Authentication
|
|
14
|
-
|
|
15
14
|
# Raised if the current authentication method is not allowed
|
|
16
15
|
class DisallowedMethod < Net::SSH::Exception
|
|
17
16
|
end
|
|
@@ -42,7 +41,7 @@ module Net
|
|
|
42
41
|
|
|
43
42
|
# Instantiates a new Authentication::Session object over the given
|
|
44
43
|
# transport layer abstraction.
|
|
45
|
-
def initialize(transport, options={})
|
|
44
|
+
def initialize(transport, options = {})
|
|
46
45
|
self.logger = transport.logger
|
|
47
46
|
@transport = transport
|
|
48
47
|
|
|
@@ -55,7 +54,7 @@ module Net
|
|
|
55
54
|
# Attempts to authenticate the given user, in preparation for the next
|
|
56
55
|
# service request. Returns true if an authentication method succeeds in
|
|
57
56
|
# authenticating the user, and false otherwise.
|
|
58
|
-
def authenticate(next_service, username, password=nil)
|
|
57
|
+
def authenticate(next_service, username, password = nil)
|
|
59
58
|
debug { "beginning authentication of `#{username}'" }
|
|
60
59
|
|
|
61
60
|
transport.send_message(transport.service_request("ssh-userauth"))
|
|
@@ -63,28 +62,30 @@ module Net
|
|
|
63
62
|
|
|
64
63
|
key_manager = KeyManager.new(logger, options)
|
|
65
64
|
keys.each { |key| key_manager.add(key) } unless keys.empty?
|
|
65
|
+
keycerts.each { |keycert| key_manager.add_keycert(keycert) } unless keycerts.empty?
|
|
66
66
|
key_data.each { |key2| key_manager.add_key_data(key2) } unless key_data.empty?
|
|
67
67
|
default_keys.each { |key| key_manager.add(key) } unless options.key?(:keys) || options.key?(:key_data)
|
|
68
68
|
|
|
69
69
|
attempted = []
|
|
70
70
|
|
|
71
71
|
@auth_methods.each do |name|
|
|
72
|
+
next unless @allowed_auth_methods.include?(name)
|
|
73
|
+
|
|
74
|
+
attempted << name
|
|
75
|
+
|
|
76
|
+
debug { "trying #{name}" }
|
|
72
77
|
begin
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
rescue NameError
|
|
81
|
-
debug {"Mechanism #{name} was requested, but isn't a known type. Ignoring it."}
|
|
82
|
-
next
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
return true if method.authenticate(next_service, username, password)
|
|
86
|
-
rescue Net::SSH::Authentication::DisallowedMethod
|
|
78
|
+
auth_class = Methods.const_get(name.split(/\W+/).map { |p| p.capitalize }.join)
|
|
79
|
+
method = auth_class.new(self,
|
|
80
|
+
key_manager: key_manager, password_prompt: options[:password_prompt],
|
|
81
|
+
pubkey_algorithms: options[:pubkey_algorithms] || nil)
|
|
82
|
+
rescue NameError
|
|
83
|
+
debug {"Mechanism #{name} was requested, but isn't a known type. Ignoring it."}
|
|
84
|
+
next
|
|
87
85
|
end
|
|
86
|
+
|
|
87
|
+
return true if method.authenticate(next_service, username, password)
|
|
88
|
+
rescue Net::SSH::Authentication::DisallowedMethod
|
|
88
89
|
end
|
|
89
90
|
|
|
90
91
|
error { "all authorization methods failed (tried #{attempted.join(', ')})" }
|
|
@@ -128,6 +129,7 @@ module Net
|
|
|
128
129
|
def expect_message(type)
|
|
129
130
|
message = next_message
|
|
130
131
|
raise Net::SSH::Exception, "expected #{type}, got #{message.type} (#{message})" unless message.type == type
|
|
132
|
+
|
|
131
133
|
message
|
|
132
134
|
end
|
|
133
135
|
|
|
@@ -136,12 +138,8 @@ module Net
|
|
|
136
138
|
# Returns an array of paths to the key files usually defined
|
|
137
139
|
# by system default.
|
|
138
140
|
def default_keys
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
~/.ssh2/id_ed25519 ~/.ssh2/id_rsa ~/.ssh2/id_dsa ~/.ssh2/id_ecdsa]
|
|
142
|
-
else
|
|
143
|
-
%w[~/.ssh/id_dsa ~/.ssh/id_rsa ~/.ssh2/id_dsa ~/.ssh2/id_rsa]
|
|
144
|
-
end
|
|
141
|
+
%w[~/.ssh/id_ed25519 ~/.ssh/id_rsa ~/.ssh/id_dsa ~/.ssh/id_ecdsa
|
|
142
|
+
~/.ssh2/id_ed25519 ~/.ssh2/id_rsa ~/.ssh2/id_dsa ~/.ssh2/id_ecdsa]
|
|
145
143
|
end
|
|
146
144
|
|
|
147
145
|
# Returns an array of paths to the key files that should be used when
|
|
@@ -150,6 +148,12 @@ module Net
|
|
|
150
148
|
Array(options[:keys])
|
|
151
149
|
end
|
|
152
150
|
|
|
151
|
+
# Returns an array of paths to the keycert files that should be used when
|
|
152
|
+
# attempting any key-based authentication mechanism.
|
|
153
|
+
def keycerts
|
|
154
|
+
Array(options[:keycerts])
|
|
155
|
+
end
|
|
156
|
+
|
|
153
157
|
# Returns an array of the key data that should be used when
|
|
154
158
|
# attempting any key-based authentication mechanism.
|
|
155
159
|
def key_data
|
data/lib/net/ssh/buffer.rb
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
require 'net/ssh/ruby_compat'
|
|
2
1
|
require 'net/ssh/transport/openssl'
|
|
3
2
|
|
|
4
3
|
require 'net/ssh/authentication/certificate'
|
|
@@ -6,7 +5,6 @@ require 'net/ssh/authentication/ed25519_loader'
|
|
|
6
5
|
|
|
7
6
|
module Net
|
|
8
7
|
module SSH
|
|
9
|
-
|
|
10
8
|
# Net::SSH::Buffer is a flexible class for building and parsing binary
|
|
11
9
|
# data packets. It provides a stream-like interface for sequentially
|
|
12
10
|
# reading data items from the buffer, as well as a useful helper method
|
|
@@ -72,7 +70,7 @@ module Net
|
|
|
72
70
|
|
|
73
71
|
# Creates a new buffer, initialized to the given content. The position
|
|
74
72
|
# is initialized to the beginning of the buffer.
|
|
75
|
-
def initialize(content=
|
|
73
|
+
def initialize(content = String.new)
|
|
76
74
|
@content = content.to_s
|
|
77
75
|
@position = 0
|
|
78
76
|
end
|
|
@@ -119,7 +117,7 @@ module Net
|
|
|
119
117
|
# Resets the buffer, making it empty. Also, resets the read position to
|
|
120
118
|
# 0.
|
|
121
119
|
def clear!
|
|
122
|
-
@content =
|
|
120
|
+
@content = String.new
|
|
123
121
|
@position = 0
|
|
124
122
|
end
|
|
125
123
|
|
|
@@ -130,12 +128,12 @@ module Net
|
|
|
130
128
|
# would otherwise tend to grow without bound.
|
|
131
129
|
#
|
|
132
130
|
# Returns the buffer object itself.
|
|
133
|
-
def consume!(n=position)
|
|
131
|
+
def consume!(n = position)
|
|
134
132
|
if n >= length
|
|
135
133
|
# optimize for a fairly common case
|
|
136
134
|
clear!
|
|
137
135
|
elsif n > 0
|
|
138
|
-
@content = @content[n..-1] ||
|
|
136
|
+
@content = @content[n..-1] || String.new
|
|
139
137
|
@position -= n
|
|
140
138
|
@position = 0 if @position < 0
|
|
141
139
|
end
|
|
@@ -173,7 +171,7 @@ module Net
|
|
|
173
171
|
# Reads and returns the next +count+ bytes from the buffer, starting from
|
|
174
172
|
# the read position. If +count+ is +nil+, this will return all remaining
|
|
175
173
|
# text in the buffer. This method will increment the pointer.
|
|
176
|
-
def read(count=nil)
|
|
174
|
+
def read(count = nil)
|
|
177
175
|
count ||= length
|
|
178
176
|
count = length - @position if @position + count > length
|
|
179
177
|
@position += count
|
|
@@ -182,7 +180,7 @@ module Net
|
|
|
182
180
|
|
|
183
181
|
# Reads (as #read) and returns the given number of bytes from the buffer,
|
|
184
182
|
# and then consumes (as #consume!) all data up to the new read position.
|
|
185
|
-
def read!(count=nil)
|
|
183
|
+
def read!(count = nil)
|
|
186
184
|
data = read(count)
|
|
187
185
|
consume!
|
|
188
186
|
data
|
|
@@ -238,6 +236,7 @@ module Net
|
|
|
238
236
|
def read_bignum
|
|
239
237
|
data = read_string
|
|
240
238
|
return unless data
|
|
239
|
+
|
|
241
240
|
OpenSSL::BN.new(data, 2)
|
|
242
241
|
end
|
|
243
242
|
|
|
@@ -284,6 +283,8 @@ module Net
|
|
|
284
283
|
key.iqmp = iqmp
|
|
285
284
|
end
|
|
286
285
|
key
|
|
286
|
+
when /^ecdsa\-sha2\-(\w*)$/
|
|
287
|
+
OpenSSL::PKey::EC.read_keyblob($1, self)
|
|
287
288
|
else
|
|
288
289
|
raise Exception, "Cannot decode private key of type #{type}"
|
|
289
290
|
end
|
|
@@ -296,42 +297,47 @@ module Net
|
|
|
296
297
|
when /^(.*)-cert-v01@openssh\.com$/
|
|
297
298
|
key = Net::SSH::Authentication::Certificate.read_certblob(self, $1)
|
|
298
299
|
when /^ssh-dss$/
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
300
|
+
p = read_bignum
|
|
301
|
+
q = read_bignum
|
|
302
|
+
g = read_bignum
|
|
303
|
+
pub_key = read_bignum
|
|
304
|
+
|
|
305
|
+
asn1 = OpenSSL::ASN1::Sequence.new(
|
|
306
|
+
[
|
|
307
|
+
OpenSSL::ASN1::Sequence.new(
|
|
308
|
+
[
|
|
309
|
+
OpenSSL::ASN1::ObjectId.new('DSA'),
|
|
310
|
+
OpenSSL::ASN1::Sequence.new(
|
|
311
|
+
[
|
|
312
|
+
OpenSSL::ASN1::Integer.new(p),
|
|
313
|
+
OpenSSL::ASN1::Integer.new(q),
|
|
314
|
+
OpenSSL::ASN1::Integer.new(g)
|
|
315
|
+
]
|
|
316
|
+
)
|
|
317
|
+
]
|
|
318
|
+
),
|
|
319
|
+
OpenSSL::ASN1::BitString.new(OpenSSL::ASN1::Integer.new(pub_key).to_der)
|
|
320
|
+
]
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
key = OpenSSL::PKey::DSA.new(asn1.to_der)
|
|
312
324
|
when /^ssh-rsa$/
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
325
|
+
e = read_bignum
|
|
326
|
+
n = read_bignum
|
|
327
|
+
|
|
328
|
+
asn1 = OpenSSL::ASN1::Sequence(
|
|
329
|
+
[
|
|
330
|
+
OpenSSL::ASN1::Integer(n),
|
|
331
|
+
OpenSSL::ASN1::Integer(e)
|
|
332
|
+
]
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
key = OpenSSL::PKey::RSA.new(asn1.to_der)
|
|
322
336
|
when /^ssh-ed25519$/
|
|
323
337
|
Net::SSH::Authentication::ED25519Loader.raiseUnlessLoaded("unsupported key type `#{type}'")
|
|
324
338
|
key = Net::SSH::Authentication::ED25519::PubKey.read_keyblob(self)
|
|
325
339
|
when /^ecdsa\-sha2\-(\w*)$/
|
|
326
|
-
|
|
327
|
-
raise NotImplementedError, "unsupported key type `#{type}'"
|
|
328
|
-
else
|
|
329
|
-
begin
|
|
330
|
-
key = OpenSSL::PKey::EC.read_keyblob($1, self)
|
|
331
|
-
rescue OpenSSL::PKey::ECError
|
|
332
|
-
raise NotImplementedError, "unsupported key type `#{type}'"
|
|
333
|
-
end
|
|
334
|
-
end
|
|
340
|
+
key = OpenSSL::PKey::EC.read_keyblob($1, self)
|
|
335
341
|
else
|
|
336
342
|
raise NotImplementedError, "unsupported key type `#{type}'"
|
|
337
343
|
end
|
|
@@ -355,7 +361,12 @@ module Net
|
|
|
355
361
|
# Optimized version of write where the caller gives up ownership of string
|
|
356
362
|
# to the method. This way we can mutate the string.
|
|
357
363
|
def write_moved(string)
|
|
358
|
-
@content <<
|
|
364
|
+
@content <<
|
|
365
|
+
if string.frozen?
|
|
366
|
+
string.dup.force_encoding('BINARY')
|
|
367
|
+
else
|
|
368
|
+
string.force_encoding('BINARY')
|
|
369
|
+
end
|
|
359
370
|
self
|
|
360
371
|
end
|
|
361
372
|
|
data/lib/net/ssh/buffered_io.rb
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
require 'net/ssh/buffer'
|
|
2
2
|
require 'net/ssh/loggable'
|
|
3
|
-
require 'net/ssh/ruby_compat'
|
|
4
3
|
|
|
5
|
-
module Net
|
|
4
|
+
module Net
|
|
6
5
|
module SSH
|
|
7
|
-
|
|
8
6
|
# This module is used to extend sockets and other IO objects, to allow
|
|
9
7
|
# them to be buffered for both read and write. This abstraction makes it
|
|
10
8
|
# quite easy to write a select-based event loop
|
|
@@ -49,19 +47,19 @@ module Net
|
|
|
49
47
|
# end
|
|
50
48
|
module BufferedIo
|
|
51
49
|
include Loggable
|
|
52
|
-
|
|
50
|
+
|
|
53
51
|
# Called when the #extend is called on an object, with this module as the
|
|
54
52
|
# argument. It ensures that the modules instance variables are all properly
|
|
55
53
|
# initialized.
|
|
56
|
-
def self.extended(object)
|
|
54
|
+
def self.extended(object) # :nodoc:
|
|
57
55
|
# need to use __send__ because #send is overridden in Socket
|
|
58
56
|
object.__send__(:initialize_buffered_io)
|
|
59
57
|
end
|
|
60
|
-
|
|
58
|
+
|
|
61
59
|
# Tries to read up to +n+ bytes of data from the remote end, and appends
|
|
62
60
|
# the data to the input buffer. It returns the number of bytes read, or 0
|
|
63
61
|
# if no data was available to be read.
|
|
64
|
-
def fill(n=8192)
|
|
62
|
+
def fill(n = 8192)
|
|
65
63
|
input.consume!
|
|
66
64
|
data = recv(n)
|
|
67
65
|
debug { "read #{data.length} bytes" }
|
|
@@ -71,31 +69,31 @@ module Net
|
|
|
71
69
|
@input_errors << e
|
|
72
70
|
return 0
|
|
73
71
|
end
|
|
74
|
-
|
|
72
|
+
|
|
75
73
|
# Read up to +length+ bytes from the input buffer. If +length+ is nil,
|
|
76
74
|
# all available data is read from the buffer. (See #available.)
|
|
77
|
-
def read_available(length=nil)
|
|
75
|
+
def read_available(length = nil)
|
|
78
76
|
input.read(length || available)
|
|
79
77
|
end
|
|
80
|
-
|
|
78
|
+
|
|
81
79
|
# Returns the number of bytes available to be read from the input buffer.
|
|
82
80
|
# (See #read_available.)
|
|
83
81
|
def available
|
|
84
82
|
input.available
|
|
85
83
|
end
|
|
86
|
-
|
|
84
|
+
|
|
87
85
|
# Enqueues data in the output buffer, to be written when #send_pending
|
|
88
86
|
# is called. Note that the data is _not_ sent immediately by this method!
|
|
89
87
|
def enqueue(data)
|
|
90
88
|
output.append(data)
|
|
91
89
|
end
|
|
92
|
-
|
|
90
|
+
|
|
93
91
|
# Returns +true+ if there is data waiting in the output buffer, and
|
|
94
92
|
# +false+ otherwise.
|
|
95
93
|
def pending_write?
|
|
96
94
|
output.length > 0
|
|
97
95
|
end
|
|
98
|
-
|
|
96
|
+
|
|
99
97
|
# Sends as much of the pending output as possible. Returns +true+ if any
|
|
100
98
|
# data was sent, and +false+ otherwise.
|
|
101
99
|
def send_pending
|
|
@@ -108,7 +106,7 @@ module Net
|
|
|
108
106
|
return false
|
|
109
107
|
end
|
|
110
108
|
end
|
|
111
|
-
|
|
109
|
+
|
|
112
110
|
# Calls #send_pending repeatedly, if necessary, blocking until the output
|
|
113
111
|
# buffer is empty.
|
|
114
112
|
def wait_for_pending_sends
|
|
@@ -116,31 +114,32 @@ module Net
|
|
|
116
114
|
while output.length > 0
|
|
117
115
|
result = IO.select(nil, [self]) or next
|
|
118
116
|
next unless result[1].any?
|
|
117
|
+
|
|
119
118
|
send_pending
|
|
120
119
|
end
|
|
121
120
|
end
|
|
122
|
-
|
|
121
|
+
|
|
123
122
|
public # these methods are primarily for use in tests
|
|
124
|
-
|
|
125
|
-
def write_buffer
|
|
123
|
+
|
|
124
|
+
def write_buffer # :nodoc:
|
|
126
125
|
output.to_s
|
|
127
126
|
end
|
|
128
|
-
|
|
129
|
-
def read_buffer
|
|
127
|
+
|
|
128
|
+
def read_buffer # :nodoc:
|
|
130
129
|
input.to_s
|
|
131
130
|
end
|
|
132
|
-
|
|
131
|
+
|
|
133
132
|
private
|
|
134
|
-
|
|
133
|
+
|
|
135
134
|
#--
|
|
136
135
|
# Can't use attr_reader here (after +private+) without incurring the
|
|
137
136
|
# wrath of "ruby -w". We hates it.
|
|
138
137
|
#++
|
|
139
|
-
|
|
138
|
+
|
|
140
139
|
def input; @input; end
|
|
141
140
|
|
|
142
141
|
def output; @output; end
|
|
143
|
-
|
|
142
|
+
|
|
144
143
|
# Initializes the intput and output buffers for this object. This method
|
|
145
144
|
# is called automatically when the module is mixed into an object via
|
|
146
145
|
# Object#extend (see Net::SSH::BufferedIo.extended), but must be called
|
|
@@ -167,7 +166,7 @@ module Net
|
|
|
167
166
|
# http://github.com/net-ssh/net-ssh/tree/portfwfix
|
|
168
167
|
#
|
|
169
168
|
module ForwardedBufferedIo
|
|
170
|
-
def fill(n=8192)
|
|
169
|
+
def fill(n = 8192)
|
|
171
170
|
begin
|
|
172
171
|
super(n)
|
|
173
172
|
rescue Errno::ECONNRESET => e
|
|
@@ -182,7 +181,7 @@ module Net
|
|
|
182
181
|
end
|
|
183
182
|
end
|
|
184
183
|
end
|
|
185
|
-
|
|
184
|
+
|
|
186
185
|
def send_pending
|
|
187
186
|
begin
|
|
188
187
|
super
|
|
@@ -199,6 +198,5 @@ module Net
|
|
|
199
198
|
end
|
|
200
199
|
end
|
|
201
200
|
end
|
|
202
|
-
|
|
203
201
|
end
|
|
204
202
|
end
|
data/lib/net/ssh/config.rb
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
module Net
|
|
2
2
|
module SSH
|
|
3
|
-
|
|
4
3
|
# The Net::SSH::Config class is used to parse OpenSSH configuration files,
|
|
5
4
|
# and translates that syntax into the configuration syntax that Net::SSH
|
|
6
5
|
# understands. This lets Net::SSH scripts read their configuration (to
|
|
@@ -11,6 +10,7 @@ module Net
|
|
|
11
10
|
#
|
|
12
11
|
# * ChallengeResponseAuthentication => maps to the :auth_methods option challenge-response (then coleasced into keyboard-interactive)
|
|
13
12
|
# * KbdInteractiveAuthentication => maps to the :auth_methods keyboard-interactive
|
|
13
|
+
# * CertificateFile => maps to the :keycerts option
|
|
14
14
|
# * Ciphers => maps to the :encryption option
|
|
15
15
|
# * Compression => :compression
|
|
16
16
|
# * CompressionLevel => :compression_level
|
|
@@ -33,6 +33,7 @@ module Net
|
|
|
33
33
|
# * ProxyJump => maps to the :proxy option
|
|
34
34
|
# * PubKeyAuthentication => maps to the :auth_methods option
|
|
35
35
|
# * RekeyLimit => :rekey_limit
|
|
36
|
+
# * StrictHostKeyChecking => :verify_host_key
|
|
36
37
|
# * User => :user
|
|
37
38
|
# * UserKnownHostsFile => :user_known_hosts_file
|
|
38
39
|
# * NumberOfPasswordPrompts => :number_of_password_prompts
|
|
@@ -64,7 +65,7 @@ module Net
|
|
|
64
65
|
# given +files+ (defaulting to the list of files returned by
|
|
65
66
|
# #default_files), translates the resulting hash into the options
|
|
66
67
|
# recognized by Net::SSH, and returns them.
|
|
67
|
-
def for(host, files=expandable_default_files)
|
|
68
|
+
def for(host, files = expandable_default_files)
|
|
68
69
|
translate(files.inject({}) { |settings, file|
|
|
69
70
|
load(file, host, settings)
|
|
70
71
|
})
|
|
@@ -76,7 +77,7 @@ module Net
|
|
|
76
77
|
# ones. Returns a hash containing the OpenSSH options. (See
|
|
77
78
|
# #translate for how to convert the OpenSSH options into Net::SSH
|
|
78
79
|
# options.)
|
|
79
|
-
def load(path, host, settings={}, base_dir = nil)
|
|
80
|
+
def load(path, host, settings = {}, base_dir = nil)
|
|
80
81
|
file = File.expand_path(path)
|
|
81
82
|
base_dir ||= File.dirname(file)
|
|
82
83
|
return settings unless File.readable?(file)
|
|
@@ -128,7 +129,7 @@ module Net
|
|
|
128
129
|
block_seen = true
|
|
129
130
|
elsif !block_seen
|
|
130
131
|
case key
|
|
131
|
-
when 'identityfile'
|
|
132
|
+
when 'identityfile', 'certificatefile'
|
|
132
133
|
(globals[key] ||= []) << value
|
|
133
134
|
when 'include'
|
|
134
135
|
included_file_paths(base_dir, value).each do |file_path|
|
|
@@ -139,7 +140,7 @@ module Net
|
|
|
139
140
|
end
|
|
140
141
|
elsif block_matched
|
|
141
142
|
case key
|
|
142
|
-
when 'identityfile'
|
|
143
|
+
when 'identityfile', 'certificatefile'
|
|
143
144
|
(settings[key] ||= []) << value
|
|
144
145
|
when 'include'
|
|
145
146
|
included_file_paths(base_dir, value).each do |file_path|
|
|
@@ -149,11 +150,18 @@ module Net
|
|
|
149
150
|
settings[key] = value unless settings.key?(key)
|
|
150
151
|
end
|
|
151
152
|
end
|
|
153
|
+
|
|
154
|
+
# ProxyCommand and ProxyJump override each other so they need to be tracked togeather
|
|
155
|
+
%w[proxyjump proxycommand].each do |proxy_key|
|
|
156
|
+
if (proxy_value = settings.delete(proxy_key))
|
|
157
|
+
settings['proxy'] ||= [proxy_key, proxy_value]
|
|
158
|
+
end
|
|
159
|
+
end
|
|
152
160
|
end
|
|
153
161
|
|
|
154
162
|
globals.merge(settings) do |key, oldval, newval|
|
|
155
163
|
case key
|
|
156
|
-
when 'identityfile'
|
|
164
|
+
when 'identityfile', 'certificatefile'
|
|
157
165
|
oldval + newval
|
|
158
166
|
else
|
|
159
167
|
newval
|
|
@@ -177,36 +185,57 @@ module Net
|
|
|
177
185
|
# Filters default_files down to the files that are expandable.
|
|
178
186
|
def expandable_default_files
|
|
179
187
|
default_files.keep_if do |path|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
false
|
|
185
|
-
end
|
|
188
|
+
File.expand_path(path)
|
|
189
|
+
true
|
|
190
|
+
rescue ArgumentError
|
|
191
|
+
false
|
|
186
192
|
end
|
|
187
193
|
end
|
|
188
194
|
|
|
189
195
|
private
|
|
190
196
|
|
|
197
|
+
def translate_verify_host_key(value)
|
|
198
|
+
case value
|
|
199
|
+
when false
|
|
200
|
+
:never
|
|
201
|
+
when true
|
|
202
|
+
:always
|
|
203
|
+
when 'accept-new'
|
|
204
|
+
:accept_new
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def translate_keepalive(hash, value)
|
|
209
|
+
if value && value.to_i > 0
|
|
210
|
+
hash[:keepalive] = true
|
|
211
|
+
hash[:keepalive_interval] = value.to_i
|
|
212
|
+
else
|
|
213
|
+
hash[:keepalive] = false
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
TRANSLATE_CONFIG_KEY_RENAME_MAP = {
|
|
218
|
+
bindaddress: :bind_address,
|
|
219
|
+
compression: :compression,
|
|
220
|
+
compressionlevel: :compression_level,
|
|
221
|
+
certificatefile: :keycerts,
|
|
222
|
+
connecttimeout: :timeout,
|
|
223
|
+
forwardagent: :forward_agent,
|
|
224
|
+
identitiesonly: :keys_only,
|
|
225
|
+
identityagent: :identity_agent,
|
|
226
|
+
globalknownhostsfile: :global_known_hosts_file,
|
|
227
|
+
hostkeyalias: :host_key_alias,
|
|
228
|
+
identityfile: :keys,
|
|
229
|
+
fingerprinthash: :fingerprint_hash,
|
|
230
|
+
port: :port,
|
|
231
|
+
user: :user,
|
|
232
|
+
userknownhostsfile: :user_known_hosts_file,
|
|
233
|
+
checkhostip: :check_host_ip
|
|
234
|
+
}.freeze
|
|
191
235
|
def translate_config_key(hash, key, value, settings)
|
|
192
|
-
rename = {
|
|
193
|
-
bindaddress: :bind_address,
|
|
194
|
-
compression: :compression,
|
|
195
|
-
compressionlevel: :compression_level,
|
|
196
|
-
connecttimeout: :timeout,
|
|
197
|
-
forwardagent: :forward_agent,
|
|
198
|
-
identitiesonly: :keys_only,
|
|
199
|
-
identityagent: :identity_agent,
|
|
200
|
-
globalknownhostsfile: :global_known_hosts_file,
|
|
201
|
-
hostkeyalias: :host_key_alias,
|
|
202
|
-
identityfile: :keys,
|
|
203
|
-
fingerprinthash: :fingerprint_hash,
|
|
204
|
-
port: :port,
|
|
205
|
-
user: :user,
|
|
206
|
-
userknownhostsfile: :user_known_hosts_file,
|
|
207
|
-
checkhostip: :check_host_ip
|
|
208
|
-
}
|
|
209
236
|
case key
|
|
237
|
+
when :stricthostkeychecking
|
|
238
|
+
hash[:verify_host_key] = translate_verify_host_key(value)
|
|
210
239
|
when :ciphers
|
|
211
240
|
hash[:encryption] = value.split(/,/)
|
|
212
241
|
when :hostbasedauthentication
|
|
@@ -224,12 +253,7 @@ module Net
|
|
|
224
253
|
when :serveralivecountmax
|
|
225
254
|
hash[:keepalive_maxcount] = value.to_i if value
|
|
226
255
|
when :serveraliveinterval
|
|
227
|
-
|
|
228
|
-
hash[:keepalive] = true
|
|
229
|
-
hash[:keepalive_interval] = value.to_i
|
|
230
|
-
else
|
|
231
|
-
hash[:keepalive] = false
|
|
232
|
-
end
|
|
256
|
+
translate_keepalive(hash, value)
|
|
233
257
|
when :passwordauthentication
|
|
234
258
|
if value
|
|
235
259
|
(hash[:auth_methods] << 'password').uniq!
|
|
@@ -250,15 +274,9 @@ module Net
|
|
|
250
274
|
end
|
|
251
275
|
when :preferredauthentications
|
|
252
276
|
hash[:auth_methods] = value.split(/,/) # TODO we should place to preferred_auth_methods rather than auth_methods
|
|
253
|
-
when :
|
|
254
|
-
if
|
|
255
|
-
|
|
256
|
-
hash[:proxy] = Net::SSH::Proxy::Command.new(value)
|
|
257
|
-
end
|
|
258
|
-
when :proxyjump
|
|
259
|
-
if value
|
|
260
|
-
require 'net/ssh/proxy/jump'
|
|
261
|
-
hash[:proxy] = Net::SSH::Proxy::Jump.new(value)
|
|
277
|
+
when :proxy
|
|
278
|
+
if (proxy = setup_proxy(*value))
|
|
279
|
+
hash[:proxy] = proxy
|
|
262
280
|
end
|
|
263
281
|
when :pubkeyauthentication
|
|
264
282
|
if value
|
|
@@ -271,10 +289,25 @@ module Net
|
|
|
271
289
|
when :sendenv
|
|
272
290
|
multi_send_env = value.to_s.split(/\s+/)
|
|
273
291
|
hash[:send_env] = multi_send_env.map { |e| Regexp.new pattern2regex(e).source, false }
|
|
292
|
+
when :setenv
|
|
293
|
+
hash[:set_env] = Shellwords.split(value.to_s).map { |e| e.split '=', 2 }.to_h
|
|
274
294
|
when :numberofpasswordprompts
|
|
275
295
|
hash[:number_of_password_prompts] = value.to_i
|
|
276
|
-
when *
|
|
277
|
-
hash[
|
|
296
|
+
when *TRANSLATE_CONFIG_KEY_RENAME_MAP.keys
|
|
297
|
+
hash[TRANSLATE_CONFIG_KEY_RENAME_MAP[key]] = value
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def setup_proxy(type, value)
|
|
302
|
+
case type
|
|
303
|
+
when 'proxycommand'
|
|
304
|
+
if value !~ /^none$/
|
|
305
|
+
require 'net/ssh/proxy/command'
|
|
306
|
+
Net::SSH::Proxy::Command.new(value)
|
|
307
|
+
end
|
|
308
|
+
when 'proxyjump'
|
|
309
|
+
require 'net/ssh/proxy/jump'
|
|
310
|
+
Net::SSH::Proxy::Jump.new(value)
|
|
278
311
|
end
|
|
279
312
|
end
|
|
280
313
|
|
|
@@ -282,9 +315,9 @@ module Net
|
|
|
282
315
|
# host names.
|
|
283
316
|
def pattern2regex(pattern)
|
|
284
317
|
tail = pattern
|
|
285
|
-
prefix =
|
|
318
|
+
prefix = String.new
|
|
286
319
|
while !tail.empty? do
|
|
287
|
-
head,sep,tail = tail.partition(/[\*\?]/)
|
|
320
|
+
head, sep, tail = tail.partition(/[\*\?]/)
|
|
288
321
|
prefix = prefix + Regexp.quote(head)
|
|
289
322
|
case sep
|
|
290
323
|
when '*'
|
|
@@ -338,7 +371,7 @@ module Net
|
|
|
338
371
|
|
|
339
372
|
conditions = conditions.each_slice(2)
|
|
340
373
|
condition_matches = []
|
|
341
|
-
conditions.each do |(kind,exprs)|
|
|
374
|
+
conditions.each do |(kind, exprs)|
|
|
342
375
|
exprs = unquote(exprs)
|
|
343
376
|
|
|
344
377
|
case kind.downcase
|
|
@@ -369,6 +402,5 @@ module Net
|
|
|
369
402
|
end
|
|
370
403
|
end
|
|
371
404
|
end
|
|
372
|
-
|
|
373
405
|
end
|
|
374
406
|
end
|