net-ssh 5.0.2 → 5.1.0.rc1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: b811c98652578718dfc0112f9f89ff69b08a977b88ea28d46a9ff67cb7d1a039
4
- data.tar.gz: '0901aaa3ec0a241bbb14f1b04a004eaac36d1621b4a73751df1a67ddfc7b45c7'
2
+ SHA1:
3
+ metadata.gz: a3fb9e448901ff8c2e48d5c99298ecc3129db153
4
+ data.tar.gz: dcdb5d927ab46e316b8a87354ee0e5431c2e8aa7
5
5
  SHA512:
6
- metadata.gz: f8c2f7db919776efeed510c073430b6cf171609a74e751db6689c9b1bad58ffea22074d275c486ab614f79bc0804b799c039a55766ddc620512c8ece29a0703d
7
- data.tar.gz: b4626d64f019e7ad8714eb6ce314fa695502f39be068c0e03f03090acfa1a6c44c103c1336fa904bc63d2d853481dd413b69f9c37c6d21eaf8242abf6a28c995
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 rvm_io.ruby
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 PubKey
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
- attr_reader :sign_key
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
- _type_name = decoded.read_string
117
- pk = decoded.read_string
118
- sk = decoded.read_string
119
- _comment = decoded.read_string
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
- self.new(data,password)
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
@@ -82,6 +82,8 @@ module Net
82
82
 
83
83
  when USERAUTH_FAILURE
84
84
  return false
85
+ when USERAUTH_SUCCESS
86
+ return true
85
87
 
86
88
  else
87
89
  raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
@@ -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)
@@ -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 = $1 if 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
- conditions = condition.split(/\s+/)
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
- matching = true
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
- matching = matching && negated ^ condition_met
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
- matching
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
@@ -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::PrivKey.read(key_data, passphrase) }, [ArgumentError]
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: false, proxy: socket(options))
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
@@ -156,6 +156,8 @@ module Net
156
156
  end
157
157
 
158
158
  raise "no readers were ready for reading, and none had any incoming packets" if processed == 0 && wait != 0
159
+
160
+ [[], [], []]
159
161
  end
160
162
  end
161
163
  end
@@ -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 ssh-dss
30
- ssh-rsa-cert-v01@openssh.com
31
- ssh-rsa-cert-v00@openssh.com],
32
- kex: %w[diffie-hellman-group-exchange-sha1
33
- diffie-hellman-group1-sha1
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-group-exchange-sha256],
36
- encryption: %w[aes128-cbc 3des-cbc blowfish-cbc cast128-cbc
37
- aes192-cbc aes256-cbc rijndael-cbc@lysator.liu.se
38
- idea-cbc arcfour128 arcfour256 arcfour
39
- aes128-ctr aes192-ctr aes256-ctr
40
- cast128-ctr blowfish-ctr 3des-ctr none],
41
-
42
- hmac: %w[hmac-sha1 hmac-md5 hmac-sha1-96 hmac-md5-96
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-sha2-256 hmac-sha2-512 hmac-sha2-256-96
45
- hmac-sha2-512-96 none],
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] += %w[ecdsa-sha2-nistp256
52
- ecdsa-sha2-nistp384
53
- ecdsa-sha2-nistp521]
54
- ALGORITHMS[:host_key] += %w[ssh-ed25519] if Net::SSH::Authentication::ED25519Loader::LOADED
55
- ALGORITHMS[:kex] += %w[ecdh-sha2-nistp256
56
- ecdh-sha2-nistp384
57
- ecdh-sha2-nistp521]
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
- loop do
109
- result = IO.select([self]) or next
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, it is returned
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,
@@ -21,6 +21,13 @@ module Net
21
21
  return true
22
22
  end
23
23
  end
24
+
25
+ def verify_signature(&block)
26
+ yield
27
+ rescue HostKeyUnknown => err
28
+ err.remember_host!
29
+ return true
30
+ end
24
31
  end
25
32
 
26
33
  end
@@ -34,6 +34,10 @@ module Net
34
34
  found
35
35
  end
36
36
 
37
+ def verify_signature(&block)
38
+ yield
39
+ end
40
+
37
41
  private
38
42
 
39
43
  def process_cache_miss(host_keys, args, exc_class, message)
@@ -10,6 +10,10 @@ module Net
10
10
  def verify(arguments)
11
11
  true
12
12
  end
13
+
14
+ def verify_signature(&block)
15
+ true
16
+ end
13
17
  end
14
18
 
15
19
  end
@@ -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 = 0
52
+ MINOR = 1
53
53
 
54
54
  # The tiny component of this version of the Net::SSH library
55
- TINY = 2
55
+ TINY = 0
56
56
 
57
57
  # The prerelease component of this version of the Net::SSH library
58
58
  # nil allowed
59
- PRE = nil
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.2
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-06-17 00:00:00.000000000 Z
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: '0'
267
+ version: 1.3.1
268
268
  requirements: []
269
269
  rubyforge_project:
270
- rubygems_version: 2.7.6
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