net-ssh 5.2.0 → 7.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|