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 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