net-ssh 5.0.2 → 5.1.0.rc1
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.tar.gz.sig +0 -0
- data/.travis.yml +2 -2
- data/CHANGES.txt +12 -0
- data/README.rdoc +25 -0
- data/lib/net/ssh/authentication/agent.rb +9 -3
- data/lib/net/ssh/authentication/ed25519.rb +62 -45
- data/lib/net/ssh/authentication/key_manager.rb +1 -1
- data/lib/net/ssh/authentication/methods/publickey.rb +2 -0
- data/lib/net/ssh/buffer.rb +40 -0
- data/lib/net/ssh/config.rb +17 -5
- data/lib/net/ssh/key_factory.rb +1 -1
- data/lib/net/ssh/test.rb +6 -6
- data/lib/net/ssh/test/extensions.rb +2 -0
- data/lib/net/ssh/transport/algorithms.rb +107 -90
- data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +1 -1
- data/lib/net/ssh/transport/openssl.rb +16 -0
- data/lib/net/ssh/transport/packet_stream.rb +4 -7
- data/lib/net/ssh/transport/session.rb +26 -4
- data/lib/net/ssh/verifiers/accept_new.rb +7 -0
- data/lib/net/ssh/verifiers/always.rb +4 -0
- data/lib/net/ssh/verifiers/never.rb +4 -0
- data/lib/net/ssh/version.rb +3 -3
- metadata +5 -5
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a3fb9e448901ff8c2e48d5c99298ecc3129db153
|
4
|
+
data.tar.gz: dcdb5d927ab46e316b8a87354ee0e5431c2e8aa7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 35f1fa4f2b19ee742ae1e89656919cfbb177175e52b9ec5c1bb6b6254afd1e6341b4d6ba4044a981e381848cdfc8c2670c78ea1119344e6733822f5d03223994
|
7
|
+
data.tar.gz: 59038c63ce9f6882b95538f2c984dcbc0a8b60c66092c22224d2cb605e6bb2f3aae3bb18be6d48fa3aa2ab7e99c616413ea03d07c2118fe31b720808e6d7401d
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/.travis.yml
CHANGED
@@ -34,13 +34,13 @@ matrix:
|
|
34
34
|
|
35
35
|
install:
|
36
36
|
- export JRUBY_OPTS='--client -J-XX:+TieredCompilation -J-XX:TieredStopAtLevel=1 -Xcext.enabled=false -J-Xss2m -Xcompile.invokedynamic=false'
|
37
|
-
- sudo pip install ansible
|
37
|
+
- sudo pip install ansible urllib3 pyOpenSSL ndg-httpsclient pyasn1
|
38
38
|
- gem install bundler -v "= 1.16"
|
39
39
|
- gem list bundler
|
40
40
|
- bundle _1.16_ install
|
41
41
|
- bundle _1.16_ -v
|
42
42
|
- BUNDLE_GEMFILE=./Gemfile.noed25519 bundle _1.16_ install
|
43
|
-
- sudo ansible-galaxy install
|
43
|
+
- sudo ansible-galaxy install rvm.ruby
|
44
44
|
- sudo chown -R travis:travis /home/travis/.ansible
|
45
45
|
- ansible-playbook ./test/integration/playbook.yml -i "localhost," --become -c local -e 'no_rvm=true' -e 'myuser=travis' -e 'mygroup=travis' -e 'homedir=/home/travis'
|
46
46
|
|
data/CHANGES.txt
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
=== 5.1.0.rc1
|
2
|
+
|
3
|
+
* Support new OpenSSH private key format for rsa - bcrypt for rsa (ed25519 already supported) [#646]
|
4
|
+
* Support IdentityAgent is ssh config [Frank Groeneveld, #645]
|
5
|
+
* Improve Match processin in ssh config [Aleksandrs Ļedovskis, #642]
|
6
|
+
* Ignore signature verification when verify_host_key is never [Piotr Kliczewski, #641]
|
7
|
+
* Alg preference was changed to prefer stronger encryptions [Tray, #637]
|
8
|
+
|
9
|
+
=== 5.0.2
|
10
|
+
|
11
|
+
* fix ctr for jruby [#612]
|
12
|
+
|
1
13
|
=== 5.0.1
|
2
14
|
|
3
15
|
* default_keys were not loaded even if no keys or key_data options specified [#607]
|
data/README.rdoc
CHANGED
@@ -2,6 +2,9 @@
|
|
2
2
|
{<img src="https://badges.gitter.im/net-ssh/net-ssh.svg" alt="Join the chat at https://gitter.im/net-ssh/net-ssh">}[https://gitter.im/net-ssh/net-ssh?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge]
|
3
3
|
{<img src="https://travis-ci.org/net-ssh/net-ssh.svg?branch=master" alt="Build Status" />}[https://travis-ci.org/net-ssh/net-ssh]
|
4
4
|
{<img src="https://codecov.io/gh/net-ssh/net-ssh/branch/master/graph/badge.svg" alt="Coverage status" />}[https://codecov.io/gh/net-ssh/net-ssh]
|
5
|
+
{<img src="https://opencollective.com/net-ssh/backers/badge.svg" alt="Backers on Open Collective" />}[#backers]
|
6
|
+
{<img src="https://opencollective.com/net-ssh/sponsors/badge.svg" alt="Sponsors on Open Collective" />}[#sponsors]
|
7
|
+
|
5
8
|
|
6
9
|
= Net::SSH 5.x
|
7
10
|
|
@@ -142,6 +145,28 @@ For time to time, the public certificate associated to the private key needs to
|
|
142
145
|
mv gem-public_cert.pem net-ssh-public_cert.pem
|
143
146
|
gem cert --add net-ssh-public_cert.pem
|
144
147
|
|
148
|
+
== CREDITS
|
149
|
+
|
150
|
+
=== Contributors
|
151
|
+
|
152
|
+
This project exists thanks to all the people who contribute.
|
153
|
+
|
154
|
+
{<img src="https://opencollective.com/net-ssh/contributors.svg?width=890&button=false" />}["graphs/contributors"]
|
155
|
+
|
156
|
+
|
157
|
+
=== Backers
|
158
|
+
|
159
|
+
Thank you to all our backers! 🙏 {Become a backer}[https://opencollective.com/net-ssh#backer)]
|
160
|
+
|
161
|
+
{<img src="https://opencollective.com/net-ssh/backers.svg?width=890”>}["https://opencollective.com/net-ssh#backers"]
|
162
|
+
|
163
|
+
=== Sponsors
|
164
|
+
|
165
|
+
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. {Become a sponsor}[https://opencollective.com/net-ssh#sponsor]
|
166
|
+
{<img src="https://opencollective.com/net-ssh/sponsor/0/avatar.svg" alt="Sponsor" />}[https://opencollective.com/net-ssh/sponsor/0/website]
|
167
|
+
|
168
|
+
|
169
|
+
|
145
170
|
|
146
171
|
== LICENSE:
|
147
172
|
|
@@ -62,9 +62,9 @@ module Net
|
|
62
62
|
|
63
63
|
# Instantiates a new agent object, connects to a running SSH agent,
|
64
64
|
# negotiates the agent protocol version, and returns the agent object.
|
65
|
-
def self.connect(logger=nil, agent_socket_factory = nil)
|
65
|
+
def self.connect(logger=nil, agent_socket_factory = nil, identity_agent = nil)
|
66
66
|
agent = new(logger)
|
67
|
-
agent.connect!(agent_socket_factory)
|
67
|
+
agent.connect!(agent_socket_factory, identity_agent)
|
68
68
|
agent.negotiate!
|
69
69
|
agent
|
70
70
|
end
|
@@ -79,11 +79,13 @@ module Net
|
|
79
79
|
# given by the attribute writers. If the agent on the other end of the
|
80
80
|
# socket reports that it is an SSH2-compatible agent, this will fail
|
81
81
|
# (it only supports the ssh-agent distributed by OpenSSH).
|
82
|
-
def connect!(agent_socket_factory = nil)
|
82
|
+
def connect!(agent_socket_factory = nil, identity_agent = nil)
|
83
83
|
debug { "connecting to ssh-agent" }
|
84
84
|
@socket =
|
85
85
|
if agent_socket_factory
|
86
86
|
agent_socket_factory.call
|
87
|
+
elsif identity_agent
|
88
|
+
unix_socket_class.open(identity_agent)
|
87
89
|
elsif ENV['SSH_AUTH_SOCK'] && unix_socket_class
|
88
90
|
unix_socket_class.open(ENV['SSH_AUTH_SOCK'])
|
89
91
|
elsif Gem.win_platform? && RUBY_ENGINE != "jruby"
|
@@ -124,6 +126,10 @@ module Net
|
|
124
126
|
comment_str = body.read_string
|
125
127
|
begin
|
126
128
|
key = Buffer.new(key_str).read_key
|
129
|
+
if key.nil?
|
130
|
+
error { "ignoring invalid key: #{comment_str}" }
|
131
|
+
next
|
132
|
+
end
|
127
133
|
key.extend(Comment)
|
128
134
|
key.comment = comment_str
|
129
135
|
identities.push key
|
@@ -22,51 +22,14 @@ module Net
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
-
class
|
26
|
-
include Net::SSH::Authentication::PubKeyFingerprint
|
27
|
-
|
28
|
-
attr_reader :verify_key
|
29
|
-
|
30
|
-
def initialize(data)
|
31
|
-
@verify_key = ::Ed25519::VerifyKey.new(data)
|
32
|
-
end
|
33
|
-
|
34
|
-
def self.read_keyblob(buffer)
|
35
|
-
PubKey.new(buffer.read_string)
|
36
|
-
end
|
37
|
-
|
38
|
-
def to_blob
|
39
|
-
Net::SSH::Buffer.from(:mstring,"ssh-ed25519",:string,@verify_key.to_bytes).to_s
|
40
|
-
end
|
41
|
-
|
42
|
-
def ssh_type
|
43
|
-
"ssh-ed25519"
|
44
|
-
end
|
45
|
-
|
46
|
-
def ssh_signature_type
|
47
|
-
ssh_type
|
48
|
-
end
|
49
|
-
|
50
|
-
def ssh_do_verify(sig,data)
|
51
|
-
@verify_key.verify(sig,data)
|
52
|
-
end
|
53
|
-
|
54
|
-
def to_pem
|
55
|
-
# TODO this is not pem
|
56
|
-
ssh_type + Base64.encode64(@verify_key.to_bytes)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
class PrivKey
|
25
|
+
class OpenSSHPrivateKeyLoader
|
61
26
|
CipherFactory = Net::SSH::Transport::CipherFactory
|
62
27
|
|
63
28
|
MBEGIN = "-----BEGIN OPENSSH PRIVATE KEY-----\n"
|
64
29
|
MEND = "-----END OPENSSH PRIVATE KEY-----\n"
|
65
30
|
MAGIC = "openssh-key-v1"
|
66
31
|
|
67
|
-
|
68
|
-
|
69
|
-
def initialize(datafull,password)
|
32
|
+
def self.read(datafull, password)
|
70
33
|
raise ArgumentError.new("Expected #{MBEGIN} at start of private key") unless datafull.start_with?(MBEGIN)
|
71
34
|
raise ArgumentError.new("Expected #{MEND} at end of private key") unless datafull.end_with?(MEND)
|
72
35
|
datab64 = datafull[MBEGIN.size...-MEND.size]
|
@@ -113,10 +76,64 @@ module Net
|
|
113
76
|
|
114
77
|
raise ArgumentError, "Decrypt failed on private key" if (check1 != check2)
|
115
78
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
79
|
+
type_name = decoded.read_string
|
80
|
+
case type_name
|
81
|
+
when "ssh-ed25519"
|
82
|
+
PrivKey.new(decoded)
|
83
|
+
else
|
84
|
+
decoded.read_private_keyblob(type_name)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
class PubKey
|
90
|
+
include Net::SSH::Authentication::PubKeyFingerprint
|
91
|
+
|
92
|
+
attr_reader :verify_key
|
93
|
+
|
94
|
+
def initialize(data)
|
95
|
+
@verify_key = ::Ed25519::VerifyKey.new(data)
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.read_keyblob(buffer)
|
99
|
+
PubKey.new(buffer.read_string)
|
100
|
+
end
|
101
|
+
|
102
|
+
def to_blob
|
103
|
+
Net::SSH::Buffer.from(:mstring,"ssh-ed25519",:string,@verify_key.to_bytes).to_s
|
104
|
+
end
|
105
|
+
|
106
|
+
def ssh_type
|
107
|
+
"ssh-ed25519"
|
108
|
+
end
|
109
|
+
|
110
|
+
def ssh_signature_type
|
111
|
+
ssh_type
|
112
|
+
end
|
113
|
+
|
114
|
+
def ssh_do_verify(sig,data)
|
115
|
+
@verify_key.verify(sig,data)
|
116
|
+
end
|
117
|
+
|
118
|
+
def to_pem
|
119
|
+
# TODO this is not pem
|
120
|
+
ssh_type + Base64.encode64(@verify_key.to_bytes)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class PrivKey
|
125
|
+
CipherFactory = Net::SSH::Transport::CipherFactory
|
126
|
+
|
127
|
+
MBEGIN = "-----BEGIN OPENSSH PRIVATE KEY-----\n"
|
128
|
+
MEND = "-----END OPENSSH PRIVATE KEY-----\n"
|
129
|
+
MAGIC = "openssh-key-v1"
|
130
|
+
|
131
|
+
attr_reader :sign_key
|
132
|
+
|
133
|
+
def initialize(buffer)
|
134
|
+
pk = buffer.read_string
|
135
|
+
sk = buffer.read_string
|
136
|
+
_comment = buffer.read_string
|
120
137
|
|
121
138
|
@pk = pk
|
122
139
|
@sign_key = SigningKeyFromFile.new(pk,sk)
|
@@ -142,8 +159,8 @@ module Net
|
|
142
159
|
@sign_key.sign(data)
|
143
160
|
end
|
144
161
|
|
145
|
-
def self.read(data,password)
|
146
|
-
|
162
|
+
def self.read(data, password)
|
163
|
+
OpenSSHPrivateKeyLoader.read(data, password)
|
147
164
|
end
|
148
165
|
end
|
149
166
|
end
|
@@ -176,7 +176,7 @@ module Net
|
|
176
176
|
# or if the agent is otherwise not available.
|
177
177
|
def agent
|
178
178
|
return unless use_agent?
|
179
|
-
@agent ||= Agent.connect(logger, options[:agent_socket_factory])
|
179
|
+
@agent ||= Agent.connect(logger, options[:agent_socket_factory], options[:identity_agent])
|
180
180
|
rescue AgentNotAvailable
|
181
181
|
@use_agent = false
|
182
182
|
nil
|
data/lib/net/ssh/buffer.rb
CHANGED
@@ -249,6 +249,46 @@ module Net
|
|
249
249
|
return (type ? read_keyblob(type) : nil)
|
250
250
|
end
|
251
251
|
|
252
|
+
def read_private_keyblob(type)
|
253
|
+
case type
|
254
|
+
when /^ssh-rsa$/
|
255
|
+
key = OpenSSL::PKey::RSA.new
|
256
|
+
n = read_bignum
|
257
|
+
e = read_bignum
|
258
|
+
d = read_bignum
|
259
|
+
iqmp = read_bignum
|
260
|
+
p = read_bignum
|
261
|
+
q = read_bignum
|
262
|
+
_unkown1 = read_bignum
|
263
|
+
_unkown2 = read_bignum
|
264
|
+
dmp1 = d % (p - 1)
|
265
|
+
dmq1 = d % (q - 1)
|
266
|
+
if key.respond_to?(:set_key)
|
267
|
+
key.set_key(n, e, d)
|
268
|
+
else
|
269
|
+
key.e = e
|
270
|
+
key.n = n
|
271
|
+
key.d = d
|
272
|
+
end
|
273
|
+
if key.respond_to?(:set_factors)
|
274
|
+
key.set_factors(p, q)
|
275
|
+
else
|
276
|
+
key.p = p
|
277
|
+
key.q = q
|
278
|
+
end
|
279
|
+
if key.respond_to?(:set_crt_params)
|
280
|
+
key.set_crt_params(dmp1, dmq1, iqmp)
|
281
|
+
else
|
282
|
+
key.dmp1 = dmp1
|
283
|
+
key.dmq1 = dmq1
|
284
|
+
key.iqmp = iqmp
|
285
|
+
end
|
286
|
+
key
|
287
|
+
else
|
288
|
+
raise Exception, "Cannot decode private key of type #{type}"
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
252
292
|
# Read a keyblob of the given type from the buffer, and return it as
|
253
293
|
# a key. Only RSA, DSA, and ECDSA keys are supported.
|
254
294
|
def read_keyblob(type)
|
data/lib/net/ssh/config.rb
CHANGED
@@ -22,6 +22,7 @@ module Net
|
|
22
22
|
# * HostKeyAlias => :host_key_alias
|
23
23
|
# * HostName => :host_name
|
24
24
|
# * IdentityFile => maps to the :keys option
|
25
|
+
# * IdentityAgent => :identity_agent
|
25
26
|
# * IdentitiesOnly => :keys_only
|
26
27
|
# * Macs => maps to the :hmac option
|
27
28
|
# * PasswordAuthentication => maps to the :auth_methods option password
|
@@ -95,7 +96,7 @@ module Net
|
|
95
96
|
next if value.nil?
|
96
97
|
|
97
98
|
key.downcase!
|
98
|
-
value =
|
99
|
+
value = unquote(value)
|
99
100
|
|
100
101
|
value = case value.strip
|
101
102
|
when /^\d+$/ then value.to_i
|
@@ -194,6 +195,7 @@ module Net
|
|
194
195
|
connecttimeout: :timeout,
|
195
196
|
forwardagent: :forward_agent,
|
196
197
|
identitiesonly: :keys_only,
|
198
|
+
identityagent: :identity_agent,
|
197
199
|
globalknownhostsfile: :global_known_hosts_file,
|
198
200
|
hostkeyalias: :host_key_alias,
|
199
201
|
identityfile: :keys,
|
@@ -326,12 +328,17 @@ module Net
|
|
326
328
|
end
|
327
329
|
|
328
330
|
def eval_match_conditions(condition, host, settings)
|
329
|
-
|
331
|
+
# Not using `\s` for whitespace matching as canonical
|
332
|
+
# ssh_config parser implementation (OpenSSH) has specific character set.
|
333
|
+
# Ref: https://github.com/openssh/openssh-portable/blob/2581333d564d8697837729b3d07d45738eaf5a54/misc.c#L237-L239
|
334
|
+
conditions = condition.split(/[ \t\r\n]+|(?<!=)=(?!=)/).reject(&:empty?)
|
330
335
|
return true if conditions == ["all"]
|
331
336
|
|
332
337
|
conditions = conditions.each_slice(2)
|
333
|
-
|
338
|
+
condition_matches = []
|
334
339
|
conditions.each do |(kind,exprs)|
|
340
|
+
exprs = unquote(exprs)
|
341
|
+
|
335
342
|
case kind.downcase
|
336
343
|
when "all"
|
337
344
|
raise "all cannot be mixed with other conditions"
|
@@ -346,12 +353,17 @@ module Net
|
|
346
353
|
exprs.split(",").each do |expr|
|
347
354
|
condition_met = condition_met || host =~ pattern2regex(expr)
|
348
355
|
end
|
349
|
-
|
356
|
+
condition_matches << (true && negated ^ condition_met)
|
350
357
|
# else
|
351
358
|
# warn "net-ssh: Unsupported expr in Match block: #{kind}"
|
352
359
|
end
|
353
360
|
end
|
354
|
-
|
361
|
+
|
362
|
+
!condition_matches.empty? && condition_matches.all?
|
363
|
+
end
|
364
|
+
|
365
|
+
def unquote(string)
|
366
|
+
string =~ /^"(.*)"$/ ? Regexp.last_match(1) : string
|
355
367
|
end
|
356
368
|
end
|
357
369
|
end
|
data/lib/net/ssh/key_factory.rb
CHANGED
@@ -111,7 +111,7 @@ module Net
|
|
111
111
|
def classify_key(data, filename)
|
112
112
|
if data.match(/-----BEGIN OPENSSH PRIVATE KEY-----/)
|
113
113
|
Net::SSH::Authentication::ED25519Loader.raiseUnlessLoaded("OpenSSH keys only supported if ED25519 is available")
|
114
|
-
return ->(key_data, passphrase) { Net::SSH::Authentication::ED25519::
|
114
|
+
return ->(key_data, passphrase) { Net::SSH::Authentication::ED25519::OpenSSHPrivateKeyLoader.read(key_data, passphrase) }, [ArgumentError]
|
115
115
|
elsif OpenSSL::PKey.respond_to?(:read)
|
116
116
|
return ->(key_data, passphrase) { OpenSSL::PKey.read(key_data, passphrase) }, [ArgumentError, OpenSSL::PKey::PKeyError]
|
117
117
|
elsif data.match(/-----BEGIN DSA PRIVATE KEY-----/)
|
data/lib/net/ssh/test.rb
CHANGED
@@ -3,7 +3,7 @@ require 'net/ssh/connection/session'
|
|
3
3
|
require 'net/ssh/test/kex'
|
4
4
|
require 'net/ssh/test/socket'
|
5
5
|
|
6
|
-
module Net
|
6
|
+
module Net
|
7
7
|
module SSH
|
8
8
|
|
9
9
|
# This module may be used in unit tests, for when you want to test that your
|
@@ -54,30 +54,30 @@ module Net
|
|
54
54
|
Net::SSH::Test::Extensions::IO.with_test_extension { yield socket.script if block_given? }
|
55
55
|
return socket.script
|
56
56
|
end
|
57
|
-
|
57
|
+
|
58
58
|
# Returns the test socket instance to use for these tests (see
|
59
59
|
# Net::SSH::Test::Socket).
|
60
60
|
def socket(options={})
|
61
61
|
@socket ||= Net::SSH::Test::Socket.new
|
62
62
|
end
|
63
|
-
|
63
|
+
|
64
64
|
# Returns the connection session (Net::SSH::Connection::Session) for use
|
65
65
|
# in these tests. It is a fully functional SSH session, operating over
|
66
66
|
# a mock socket (#socket).
|
67
67
|
def connection(options={})
|
68
68
|
@connection ||= Net::SSH::Connection::Session.new(transport(options), options)
|
69
69
|
end
|
70
|
-
|
70
|
+
|
71
71
|
# Returns the transport session (Net::SSH::Transport::Session) for use
|
72
72
|
# in these tests. It is a fully functional SSH transport session, operating
|
73
73
|
# over a mock socket (#socket).
|
74
74
|
def transport(options={})
|
75
75
|
@transport ||= Net::SSH::Transport::Session.new(
|
76
76
|
options[:host] || "localhost",
|
77
|
-
options.merge(kex: "test", host_key: "ssh-rsa", verify_host_key:
|
77
|
+
options.merge(kex: "test", host_key: "ssh-rsa", verify_host_key: :never, proxy: socket(options))
|
78
78
|
)
|
79
79
|
end
|
80
|
-
|
80
|
+
|
81
81
|
# First asserts that a story has been described (see #story). Then yields,
|
82
82
|
# and then asserts that all items described in the script have been
|
83
83
|
# processed. Typically, this is called immediately after a story has
|
@@ -8,8 +8,8 @@ require 'net/ssh/transport/kex'
|
|
8
8
|
require 'net/ssh/transport/server_version'
|
9
9
|
require 'net/ssh/authentication/ed25519_loader'
|
10
10
|
|
11
|
-
module Net
|
12
|
-
module SSH
|
11
|
+
module Net
|
12
|
+
module SSH
|
13
13
|
module Transport
|
14
14
|
|
15
15
|
# Implements the higher-level logic behind an SSH key-exchange. It handles
|
@@ -22,92 +22,109 @@ module Net
|
|
22
22
|
class Algorithms
|
23
23
|
include Loggable
|
24
24
|
include Constants
|
25
|
-
|
25
|
+
|
26
26
|
# Define the default algorithms, in order of preference, supported by
|
27
27
|
# Net::SSH.
|
28
28
|
ALGORITHMS = {
|
29
|
-
host_key: %w[ssh-rsa
|
30
|
-
ssh-rsa-cert-
|
31
|
-
ssh-rsa-
|
32
|
-
kex: %w[diffie-hellman-group-exchange-
|
33
|
-
diffie-hellman-
|
29
|
+
host_key: %w[ssh-rsa-cert-v01@openssh.com
|
30
|
+
ssh-rsa-cert-v00@openssh.com
|
31
|
+
ssh-rsa ssh-dss],
|
32
|
+
kex: %w[diffie-hellman-group-exchange-sha256
|
33
|
+
diffie-hellman-group-exchange-sha1
|
34
34
|
diffie-hellman-group14-sha1
|
35
|
-
diffie-hellman-
|
36
|
-
encryption: %w[
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
cast128-ctr
|
41
|
-
|
42
|
-
|
35
|
+
diffie-hellman-group1-sha1],
|
36
|
+
encryption: %w[aes256-ctr aes192-ctr aes128-ctr
|
37
|
+
aes256-cbc aes192-cbc aes128-cbc
|
38
|
+
rijndael-cbc@lysator.liu.se
|
39
|
+
blowfish-ctr blowfish-cbc
|
40
|
+
cast128-ctr cast128-cbc
|
41
|
+
3des-ctr 3des-cbc
|
42
|
+
idea-cbc arcfour256 arcfour128 arcfour
|
43
|
+
none],
|
44
|
+
|
45
|
+
hmac: %w[hmac-sha2-512 hmac-sha2-256
|
46
|
+
hmac-sha2-512-96 hmac-sha2-256-96
|
47
|
+
hmac-sha1 hmac-sha1-96
|
43
48
|
hmac-ripemd160 hmac-ripemd160@openssh.com
|
44
|
-
hmac-
|
45
|
-
|
46
|
-
|
49
|
+
hmac-md5 hmac-md5-96
|
50
|
+
none],
|
51
|
+
|
47
52
|
compression: %w[none zlib@openssh.com zlib],
|
48
53
|
language: %w[]
|
49
54
|
}
|
50
55
|
if defined?(OpenSSL::PKey::EC)
|
51
|
-
ALGORITHMS[:host_key]
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
56
|
+
ALGORITHMS[:host_key].unshift(
|
57
|
+
"ecdsa-sha2-nistp521-cert-v01@openssh.com",
|
58
|
+
"ecdsa-sha2-nistp384-cert-v01@openssh.com",
|
59
|
+
"ecdsa-sha2-nistp256-cert-v01@openssh.com",
|
60
|
+
"ecdsa-sha2-nistp521",
|
61
|
+
"ecdsa-sha2-nistp384",
|
62
|
+
"ecdsa-sha2-nistp256"
|
63
|
+
)
|
64
|
+
if Net::SSH::Authentication::ED25519Loader::LOADED
|
65
|
+
ALGORITHMS[:host_key].unshift(
|
66
|
+
"ssh-ed25519-cert-v01@openssh.com",
|
67
|
+
"ssh-ed25519"
|
68
|
+
)
|
69
|
+
end
|
70
|
+
ALGORITHMS[:kex].unshift(
|
71
|
+
"ecdh-sha2-nistp521",
|
72
|
+
"ecdh-sha2-nistp384",
|
73
|
+
"ecdh-sha2-nistp256"
|
74
|
+
)
|
58
75
|
end
|
59
|
-
|
76
|
+
|
60
77
|
# The underlying transport layer session that supports this object
|
61
78
|
attr_reader :session
|
62
|
-
|
79
|
+
|
63
80
|
# The hash of options used to initialize this object
|
64
81
|
attr_reader :options
|
65
|
-
|
82
|
+
|
66
83
|
# The kex algorithm to use settled on between the client and server.
|
67
84
|
attr_reader :kex
|
68
|
-
|
85
|
+
|
69
86
|
# The type of host key that will be used for this session.
|
70
87
|
attr_reader :host_key
|
71
|
-
|
88
|
+
|
72
89
|
# The type of the cipher to use to encrypt packets sent from the client to
|
73
90
|
# the server.
|
74
91
|
attr_reader :encryption_client
|
75
|
-
|
92
|
+
|
76
93
|
# The type of the cipher to use to decrypt packets arriving from the server.
|
77
94
|
attr_reader :encryption_server
|
78
|
-
|
95
|
+
|
79
96
|
# The type of HMAC to use to sign packets sent by the client.
|
80
97
|
attr_reader :hmac_client
|
81
|
-
|
98
|
+
|
82
99
|
# The type of HMAC to use to validate packets arriving from the server.
|
83
100
|
attr_reader :hmac_server
|
84
|
-
|
101
|
+
|
85
102
|
# The type of compression to use to compress packets being sent by the client.
|
86
103
|
attr_reader :compression_client
|
87
|
-
|
104
|
+
|
88
105
|
# The type of compression to use to decompress packets arriving from the server.
|
89
106
|
attr_reader :compression_server
|
90
|
-
|
107
|
+
|
91
108
|
# The language that will be used in messages sent by the client.
|
92
109
|
attr_reader :language_client
|
93
|
-
|
110
|
+
|
94
111
|
# The language that will be used in messages sent from the server.
|
95
112
|
attr_reader :language_server
|
96
|
-
|
113
|
+
|
97
114
|
# The hash of algorithms preferred by the client, which will be told to
|
98
115
|
# the server during algorithm negotiation.
|
99
116
|
attr_reader :algorithms
|
100
|
-
|
117
|
+
|
101
118
|
# The session-id for this session, as decided during the initial key exchange.
|
102
119
|
attr_reader :session_id
|
103
|
-
|
120
|
+
|
104
121
|
# Returns true if the given packet can be processed during a key-exchange.
|
105
122
|
def self.allowed_packet?(packet)
|
106
123
|
(1..4).include?(packet.type) ||
|
107
124
|
(6..19).include?(packet.type) ||
|
108
125
|
(21..49).include?(packet.type)
|
109
126
|
end
|
110
|
-
|
127
|
+
|
111
128
|
# Instantiates a new Algorithms object, and prepares the hash of preferred
|
112
129
|
# algorithms based on the options parameter and the ALGORITHMS constant.
|
113
130
|
def initialize(session, options={})
|
@@ -119,13 +136,13 @@ module Net
|
|
119
136
|
@client_packet = @server_packet = nil
|
120
137
|
prepare_preferred_algorithms!
|
121
138
|
end
|
122
|
-
|
139
|
+
|
123
140
|
# Start the algorithm negotation
|
124
141
|
def start
|
125
142
|
raise ArgumentError, "Cannot call start if it's negotiation started or done" if @pending || @initialized
|
126
143
|
send_kexinit
|
127
144
|
end
|
128
|
-
|
145
|
+
|
129
146
|
# Request a rekey operation. This will return immediately, and does not
|
130
147
|
# actually perform the rekey operation. It does cause the session to change
|
131
148
|
# state, however--until the key exchange finishes, no new packets will be
|
@@ -135,7 +152,7 @@ module Net
|
|
135
152
|
@initialized = false
|
136
153
|
send_kexinit
|
137
154
|
end
|
138
|
-
|
155
|
+
|
139
156
|
# Called by the transport layer when a KEXINIT packet is received, indicating
|
140
157
|
# that the server wants to exchange keys. This can be spontaneous, or it
|
141
158
|
# can be in response to a client-initiated rekey request (see #rekey!). Either
|
@@ -150,13 +167,13 @@ module Net
|
|
150
167
|
proceed!
|
151
168
|
end
|
152
169
|
end
|
153
|
-
|
170
|
+
|
154
171
|
# A convenience method for accessing the list of preferred types for a
|
155
172
|
# specific algorithm (see #algorithms).
|
156
173
|
def [](key)
|
157
174
|
algorithms[key]
|
158
175
|
end
|
159
|
-
|
176
|
+
|
160
177
|
# Returns +true+ if a key-exchange is pending. This will be true from the
|
161
178
|
# moment either the client or server requests the key exchange, until the
|
162
179
|
# exchange completes. While an exchange is pending, only a limited number
|
@@ -201,7 +218,7 @@ module Net
|
|
201
218
|
session.send_message(packet)
|
202
219
|
proceed! if @server_packet
|
203
220
|
end
|
204
|
-
|
221
|
+
|
205
222
|
# After both client and server have sent their KEXINIT packets, this
|
206
223
|
# will do the algorithm negotiation and key exchange. Once both finish,
|
207
224
|
# the object leaves the pending state and the method returns.
|
@@ -211,7 +228,7 @@ module Net
|
|
211
228
|
exchange_keys
|
212
229
|
@pending = false
|
213
230
|
end
|
214
|
-
|
231
|
+
|
215
232
|
# Prepares the list of preferred algorithms, based on the options hash
|
216
233
|
# that was given when the object was constructed, and the ALGORITHMS
|
217
234
|
# constant. Also, when determining the host_key type to use, the known
|
@@ -220,23 +237,23 @@ module Net
|
|
220
237
|
# communicating with this server.
|
221
238
|
def prepare_preferred_algorithms!
|
222
239
|
options[:compression] = %w[zlib@openssh.com zlib] if options[:compression] == true
|
223
|
-
|
240
|
+
|
224
241
|
ALGORITHMS.each do |algorithm, supported|
|
225
242
|
algorithms[algorithm] = compose_algorithm_list(supported, options[algorithm], options[:append_all_supported_algorithms])
|
226
243
|
end
|
227
|
-
|
244
|
+
|
228
245
|
# for convention, make sure our list has the same keys as the server
|
229
246
|
# list
|
230
|
-
|
247
|
+
|
231
248
|
algorithms[:encryption_client ] = algorithms[:encryption_server ] = algorithms[:encryption]
|
232
249
|
algorithms[:hmac_client ] = algorithms[:hmac_server ] = algorithms[:hmac]
|
233
250
|
algorithms[:compression_client] = algorithms[:compression_server] = algorithms[:compression]
|
234
251
|
algorithms[:language_client ] = algorithms[:language_server ] = algorithms[:language]
|
235
|
-
|
252
|
+
|
236
253
|
if !options.key?(:host_key)
|
237
254
|
# make sure the host keys are specified in preference order, where any
|
238
255
|
# existing known key for the host has preference.
|
239
|
-
|
256
|
+
|
240
257
|
existing_keys = session.host_keys
|
241
258
|
host_keys = existing_keys.map { |key| key.ssh_type }.uniq
|
242
259
|
algorithms[:host_key].each do |name|
|
@@ -245,14 +262,14 @@ module Net
|
|
245
262
|
algorithms[:host_key] = host_keys
|
246
263
|
end
|
247
264
|
end
|
248
|
-
|
265
|
+
|
249
266
|
# Composes the list of algorithms by taking supported algorithms and matching with supplied options.
|
250
267
|
def compose_algorithm_list(supported, option, append_all_supported_algorithms = false)
|
251
268
|
return supported.dup unless option
|
252
|
-
|
269
|
+
|
253
270
|
list = []
|
254
271
|
option = Array(option).compact.uniq
|
255
|
-
|
272
|
+
|
256
273
|
if option.first && option.first.start_with?('+')
|
257
274
|
list = supported.dup
|
258
275
|
list << option.first[1..-1]
|
@@ -260,30 +277,30 @@ module Net
|
|
260
277
|
list.uniq!
|
261
278
|
else
|
262
279
|
list = option
|
263
|
-
|
280
|
+
|
264
281
|
if append_all_supported_algorithms
|
265
282
|
supported.each { |name| list << name unless list.include?(name) }
|
266
283
|
end
|
267
284
|
end
|
268
|
-
|
285
|
+
|
269
286
|
unsupported = []
|
270
287
|
list.select! do |name|
|
271
288
|
is_supported = supported.include?(name)
|
272
289
|
unsupported << name unless is_supported
|
273
290
|
is_supported
|
274
291
|
end
|
275
|
-
|
292
|
+
|
276
293
|
lwarn { %(unsupported algorithm: `#{unsupported}') } unless unsupported.empty?
|
277
|
-
|
294
|
+
|
278
295
|
list
|
279
296
|
end
|
280
|
-
|
297
|
+
|
281
298
|
# Parses a KEXINIT packet from the server.
|
282
299
|
def parse_server_algorithm_packet(packet)
|
283
300
|
data = { raw: packet.content }
|
284
|
-
|
301
|
+
|
285
302
|
packet.read(16) # skip the cookie value
|
286
|
-
|
303
|
+
|
287
304
|
data[:kex] = packet.read_string.split(/,/)
|
288
305
|
data[:host_key] = packet.read_string.split(/,/)
|
289
306
|
data[:encryption_client] = packet.read_string.split(/,/)
|
@@ -294,15 +311,15 @@ module Net
|
|
294
311
|
data[:compression_server] = packet.read_string.split(/,/)
|
295
312
|
data[:language_client] = packet.read_string.split(/,/)
|
296
313
|
data[:language_server] = packet.read_string.split(/,/)
|
297
|
-
|
314
|
+
|
298
315
|
# TODO: if first_kex_packet_follows, we need to try to skip the
|
299
316
|
# actual kexinit stuff and try to guess what the server is doing...
|
300
317
|
# need to read more about this scenario.
|
301
318
|
# first_kex_packet_follows = packet.read_bool
|
302
|
-
|
319
|
+
|
303
320
|
return data
|
304
321
|
end
|
305
|
-
|
322
|
+
|
306
323
|
# Given the #algorithms map of preferred algorithm types, this constructs
|
307
324
|
# a KEXINIT packet to send to the server. It does not actually send it,
|
308
325
|
# it simply builds the packet and returns it.
|
@@ -313,14 +330,14 @@ module Net
|
|
313
330
|
hmac = algorithms[:hmac].join(",")
|
314
331
|
compression = algorithms[:compression].join(",")
|
315
332
|
language = algorithms[:language].join(",")
|
316
|
-
|
333
|
+
|
317
334
|
Net::SSH::Buffer.from(:byte, KEXINIT,
|
318
335
|
:long, [rand(0xFFFFFFFF), rand(0xFFFFFFFF), rand(0xFFFFFFFF), rand(0xFFFFFFFF)],
|
319
336
|
:mstring, [kex, host_key, encryption, encryption, hmac, hmac],
|
320
337
|
:mstring, [compression, compression, language, language],
|
321
338
|
:bool, false, :long, 0)
|
322
339
|
end
|
323
|
-
|
340
|
+
|
324
341
|
# Given the parsed server KEX packet, and the client's preferred algorithm
|
325
342
|
# lists in #algorithms, determine which preferred algorithms each has
|
326
343
|
# in common and set those as the selected algorithms. If, for any algorithm,
|
@@ -336,7 +353,7 @@ module Net
|
|
336
353
|
@compression_server = negotiate(:compression_server)
|
337
354
|
@language_client = negotiate(:language_client) rescue ""
|
338
355
|
@language_server = negotiate(:language_server) rescue ""
|
339
|
-
|
356
|
+
|
340
357
|
debug do
|
341
358
|
"negotiated:\n" +
|
342
359
|
%i[kex host_key encryption_server encryption_client hmac_client hmac_server compression_client compression_server language_client language_server].map do |key|
|
@@ -344,40 +361,40 @@ module Net
|
|
344
361
|
end.join("\n")
|
345
362
|
end
|
346
363
|
end
|
347
|
-
|
364
|
+
|
348
365
|
# Negotiates a single algorithm based on the preferences reported by the
|
349
366
|
# server and those set by the client. This is called by
|
350
367
|
# #negotiate_algorithms.
|
351
368
|
def negotiate(algorithm)
|
352
369
|
match = self[algorithm].find { |item| @server_data[algorithm].include?(item) }
|
353
|
-
|
370
|
+
|
354
371
|
raise Net::SSH::Exception, "could not settle on #{algorithm} algorithm" if match.nil?
|
355
|
-
|
372
|
+
|
356
373
|
return match
|
357
374
|
end
|
358
|
-
|
375
|
+
|
359
376
|
# Considers the sizes of the keys and block-sizes for the selected ciphers,
|
360
377
|
# and the lengths of the hmacs, and returns the largest as the byte requirement
|
361
378
|
# for the key-exchange algorithm.
|
362
379
|
def kex_byte_requirement
|
363
380
|
sizes = [8] # require at least 8 bytes
|
364
|
-
|
381
|
+
|
365
382
|
sizes.concat(CipherFactory.get_lengths(encryption_client))
|
366
383
|
sizes.concat(CipherFactory.get_lengths(encryption_server))
|
367
|
-
|
384
|
+
|
368
385
|
sizes << HMAC.key_length(hmac_client)
|
369
386
|
sizes << HMAC.key_length(hmac_server)
|
370
|
-
|
387
|
+
|
371
388
|
sizes.max
|
372
389
|
end
|
373
|
-
|
390
|
+
|
374
391
|
# Instantiates one of the Transport::Kex classes (based on the negotiated
|
375
392
|
# kex algorithm), and uses it to exchange keys. Then, the ciphers and
|
376
393
|
# HMACs are initialized and fed to the transport layer, to be used in
|
377
394
|
# further communication with the server.
|
378
395
|
def exchange_keys
|
379
396
|
debug { "exchanging keys" }
|
380
|
-
|
397
|
+
|
381
398
|
algorithm = Kex::MAP[kex].new(self, session,
|
382
399
|
client_version_string: Net::SSH::Transport::ServerVersion::PROTO_VERSION,
|
383
400
|
server_version_string: session.server_version.version,
|
@@ -387,46 +404,46 @@ module Net
|
|
387
404
|
minimum_dh_bits: options[:minimum_dh_bits],
|
388
405
|
logger: logger)
|
389
406
|
result = algorithm.exchange_keys
|
390
|
-
|
407
|
+
|
391
408
|
secret = result[:shared_secret].to_ssh
|
392
409
|
hash = result[:session_id]
|
393
410
|
digester = result[:hashing_algorithm]
|
394
|
-
|
411
|
+
|
395
412
|
@session_id ||= hash
|
396
|
-
|
413
|
+
|
397
414
|
key = Proc.new { |salt| digester.digest(secret + hash + salt + @session_id) }
|
398
|
-
|
415
|
+
|
399
416
|
iv_client = key["A"]
|
400
417
|
iv_server = key["B"]
|
401
418
|
key_client = key["C"]
|
402
419
|
key_server = key["D"]
|
403
420
|
mac_key_client = key["E"]
|
404
421
|
mac_key_server = key["F"]
|
405
|
-
|
422
|
+
|
406
423
|
parameters = { shared: secret, hash: hash, digester: digester }
|
407
|
-
|
424
|
+
|
408
425
|
cipher_client = CipherFactory.get(encryption_client, parameters.merge(iv: iv_client, key: key_client, encrypt: true))
|
409
426
|
cipher_server = CipherFactory.get(encryption_server, parameters.merge(iv: iv_server, key: key_server, decrypt: true))
|
410
|
-
|
427
|
+
|
411
428
|
mac_client = HMAC.get(hmac_client, mac_key_client, parameters)
|
412
429
|
mac_server = HMAC.get(hmac_server, mac_key_server, parameters)
|
413
|
-
|
430
|
+
|
414
431
|
session.configure_client cipher: cipher_client, hmac: mac_client,
|
415
432
|
compression: normalize_compression_name(compression_client),
|
416
433
|
compression_level: options[:compression_level],
|
417
434
|
rekey_limit: options[:rekey_limit],
|
418
435
|
max_packets: options[:rekey_packet_limit],
|
419
436
|
max_blocks: options[:rekey_blocks_limit]
|
420
|
-
|
437
|
+
|
421
438
|
session.configure_server cipher: cipher_server, hmac: mac_server,
|
422
439
|
compression: normalize_compression_name(compression_server),
|
423
440
|
rekey_limit: options[:rekey_limit],
|
424
441
|
max_packets: options[:rekey_packet_limit],
|
425
442
|
max_blocks: options[:rekey_blocks_limit]
|
426
|
-
|
443
|
+
|
427
444
|
@initialized = true
|
428
445
|
end
|
429
|
-
|
446
|
+
|
430
447
|
# Given the SSH name for some compression algorithm, return a normalized
|
431
448
|
# name as a symbol.
|
432
449
|
def normalize_compression_name(name)
|
@@ -206,7 +206,7 @@ module Net
|
|
206
206
|
|
207
207
|
hash = @digester.digest(response.to_s)
|
208
208
|
|
209
|
-
raise Net::SSH::Exception, "could not verify server signature" unless result[:server_key].ssh_do_verify(result[:server_sig], hash)
|
209
|
+
raise Net::SSH::Exception, "could not verify server signature" unless connection.host_key_verifier.verify_signature { result[:server_key].ssh_do_verify(result[:server_sig], hash) }
|
210
210
|
|
211
211
|
return hash
|
212
212
|
end
|
@@ -225,6 +225,22 @@ module OpenSSL
|
|
225
225
|
|
226
226
|
return Net::SSH::Buffer.from(:bignum, sig_r, :bignum, sig_s).to_s
|
227
227
|
end
|
228
|
+
|
229
|
+
class Point
|
230
|
+
# Returns the description of this key type used by the
|
231
|
+
# SSH2 protocol, like "ecdsa-sha2-nistp256"
|
232
|
+
def ssh_type
|
233
|
+
"ecdsa-sha2-#{CurveNameAliasInv[self.group.curve_name]}"
|
234
|
+
end
|
235
|
+
|
236
|
+
# Converts the key to a blob, according to the SSH2 protocol.
|
237
|
+
def to_blob
|
238
|
+
@blob ||= Net::SSH::Buffer.from(:string, ssh_type,
|
239
|
+
:string, CurveNameAliasInv[self.group.curve_name],
|
240
|
+
:mstring, self.to_bn.to_s(2)).to_s
|
241
|
+
@blob
|
242
|
+
end
|
243
|
+
end
|
228
244
|
end
|
229
245
|
else
|
230
246
|
class OpenSSL::PKey::ECError < RuntimeError
|
@@ -81,8 +81,8 @@ module Net
|
|
81
81
|
# default), then this will return immediately, whether a packet is
|
82
82
|
# available or not, and will return nil if there is no packet ready to be
|
83
83
|
# returned. If the mode parameter is :block, then this method will block
|
84
|
-
# until a packet is available.
|
85
|
-
def next_packet(mode=:nonblock)
|
84
|
+
# until a packet is available or timeout seconds have passed.
|
85
|
+
def next_packet(mode=:nonblock, timeout=nil)
|
86
86
|
case mode
|
87
87
|
when :nonblock then
|
88
88
|
packet = poll_next_packet
|
@@ -105,11 +105,8 @@ module Net
|
|
105
105
|
packet = poll_next_packet
|
106
106
|
return packet if packet
|
107
107
|
|
108
|
-
|
109
|
-
|
110
|
-
break if result.first.any?
|
111
|
-
end
|
112
|
-
|
108
|
+
result = IO.select([self], nil, nil, timeout)
|
109
|
+
raise Net::SSH::ConnectionTimeout, "timeout waiting for next packet" unless result
|
113
110
|
raise Net::SSH::Disconnect, "connection closed by remote host" if fill <= 0
|
114
111
|
end
|
115
112
|
|
@@ -190,7 +190,7 @@ module Net
|
|
190
190
|
loop do
|
191
191
|
return @queue.shift if consume_queue && @queue.any? && algorithms.allow?(@queue.first)
|
192
192
|
|
193
|
-
packet = socket.next_packet(mode)
|
193
|
+
packet = socket.next_packet(mode, options[:timeout])
|
194
194
|
return nil if packet.nil?
|
195
195
|
|
196
196
|
case packet.type
|
@@ -274,6 +274,23 @@ module Net
|
|
274
274
|
|
275
275
|
private
|
276
276
|
|
277
|
+
# Compatibility verifier which allows users to keep using
|
278
|
+
# custom verifier code without adding new :verify_signature
|
279
|
+
# method.
|
280
|
+
class CompatibleVerifier
|
281
|
+
def initialize(verifier)
|
282
|
+
@verifier
|
283
|
+
end
|
284
|
+
|
285
|
+
def verify(arguments)
|
286
|
+
@verifier.verify(arguments)
|
287
|
+
end
|
288
|
+
|
289
|
+
def verify_signature(&block)
|
290
|
+
yield
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
277
294
|
# Instantiates a new host-key verification class, based on the value of
|
278
295
|
# the parameter.
|
279
296
|
#
|
@@ -285,8 +302,8 @@ module Net
|
|
285
302
|
# - :accept_new (insecure)
|
286
303
|
# - :always (secure)
|
287
304
|
#
|
288
|
-
# If the argument happens to respond to :verify
|
289
|
-
# directly. Otherwise, an exception is raised.
|
305
|
+
# If the argument happens to respond to :verify and :verify_signature,
|
306
|
+
# it is returned directly. Otherwise, an exception is raised.
|
290
307
|
#
|
291
308
|
# Values false, true, and :very were deprecated in
|
292
309
|
# [#595](https://github.com/net-ssh/net-ssh/pull/595)
|
@@ -314,7 +331,12 @@ module Net
|
|
314
331
|
Net::SSH::Verifiers::Always.new
|
315
332
|
else
|
316
333
|
if verifier.respond_to?(:verify)
|
317
|
-
verifier
|
334
|
+
if verifier.respond_to?(:verify_signature)
|
335
|
+
verifier
|
336
|
+
else
|
337
|
+
Kernel.warn("Warning: verifier without :verify_signature is deprecated")
|
338
|
+
CompatibleVerifier.new(verifier)
|
339
|
+
end
|
318
340
|
else
|
319
341
|
raise(
|
320
342
|
ArgumentError,
|
data/lib/net/ssh/version.rb
CHANGED
@@ -49,14 +49,14 @@ module Net
|
|
49
49
|
MAJOR = 5
|
50
50
|
|
51
51
|
# The minor component of this version of the Net::SSH library
|
52
|
-
MINOR =
|
52
|
+
MINOR = 1
|
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
|
59
|
-
PRE =
|
59
|
+
PRE = "rc1"
|
60
60
|
|
61
61
|
# The current version of the Net::SSH library as a Version instance
|
62
62
|
CURRENT = new(*[MAJOR, MINOR, TINY, PRE].compact)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: net-ssh
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.0.
|
4
|
+
version: 5.1.0.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jamis Buck
|
@@ -32,7 +32,7 @@ cert_chain:
|
|
32
32
|
ZFwoIuXKeDmTTpryd/vI7sdLXDuV6MbWOLGh6gXn9RDDXG1EqEXW0bjovATBMpdH
|
33
33
|
9OGohJvAFzcvhDTWPwT6w3PG5B80pqb9j1hEAg==
|
34
34
|
-----END CERTIFICATE-----
|
35
|
-
date: 2018-
|
35
|
+
date: 2018-12-12 00:00:00.000000000 Z
|
36
36
|
dependencies:
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
38
|
name: bcrypt_pbkdf
|
@@ -262,12 +262,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
262
262
|
version: 2.2.6
|
263
263
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
264
264
|
requirements:
|
265
|
-
- - "
|
265
|
+
- - ">"
|
266
266
|
- !ruby/object:Gem::Version
|
267
|
-
version:
|
267
|
+
version: 1.3.1
|
268
268
|
requirements: []
|
269
269
|
rubyforge_project:
|
270
|
-
rubygems_version: 2.
|
270
|
+
rubygems_version: 2.6.8
|
271
271
|
signing_key:
|
272
272
|
specification_version: 4
|
273
273
|
summary: 'Net::SSH: a pure-Ruby implementation of the SSH2 client protocol.'
|
metadata.gz.sig
CHANGED
Binary file
|