net-ssh 6.2.0.rc1 → 7.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.dockerignore +6 -0
  4. data/.github/config/rubocop_linter_action.yml +4 -0
  5. data/.github/workflows/ci-with-docker.yml +44 -0
  6. data/.github/workflows/ci.yml +87 -0
  7. data/.github/workflows/rubocop.yml +13 -0
  8. data/.rubocop.yml +12 -1
  9. data/.rubocop_todo.yml +470 -262
  10. data/CHANGES.txt +7 -1
  11. data/Dockerfile +27 -0
  12. data/Dockerfile.openssl3 +17 -0
  13. data/Gemfile +2 -0
  14. data/Gemfile.noed25519 +2 -0
  15. data/README.md +9 -3
  16. data/Rakefile +5 -0
  17. data/docker-compose.yml +23 -0
  18. data/lib/net/ssh/authentication/agent.rb +17 -15
  19. data/lib/net/ssh/authentication/certificate.rb +7 -5
  20. data/lib/net/ssh/authentication/constants.rb +0 -1
  21. data/lib/net/ssh/authentication/ed25519.rb +10 -6
  22. data/lib/net/ssh/authentication/ed25519_loader.rb +4 -7
  23. data/lib/net/ssh/authentication/key_manager.rb +46 -34
  24. data/lib/net/ssh/authentication/methods/abstract.rb +12 -3
  25. data/lib/net/ssh/authentication/methods/hostbased.rb +3 -5
  26. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +2 -2
  27. data/lib/net/ssh/authentication/methods/none.rb +6 -9
  28. data/lib/net/ssh/authentication/methods/password.rb +2 -3
  29. data/lib/net/ssh/authentication/methods/publickey.rb +56 -16
  30. data/lib/net/ssh/authentication/pageant.rb +97 -97
  31. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +2 -2
  32. data/lib/net/ssh/authentication/session.rb +18 -17
  33. data/lib/net/ssh/buffer.rb +50 -30
  34. data/lib/net/ssh/buffered_io.rb +24 -25
  35. data/lib/net/ssh/config.rb +33 -20
  36. data/lib/net/ssh/connection/channel.rb +85 -82
  37. data/lib/net/ssh/connection/constants.rb +0 -4
  38. data/lib/net/ssh/connection/event_loop.rb +30 -24
  39. data/lib/net/ssh/connection/keepalive.rb +12 -12
  40. data/lib/net/ssh/connection/session.rb +108 -107
  41. data/lib/net/ssh/connection/term.rb +56 -58
  42. data/lib/net/ssh/errors.rb +12 -12
  43. data/lib/net/ssh/key_factory.rb +7 -8
  44. data/lib/net/ssh/known_hosts.rb +84 -15
  45. data/lib/net/ssh/loggable.rb +8 -9
  46. data/lib/net/ssh/packet.rb +1 -1
  47. data/lib/net/ssh/prompt.rb +9 -11
  48. data/lib/net/ssh/proxy/command.rb +1 -1
  49. data/lib/net/ssh/proxy/errors.rb +2 -4
  50. data/lib/net/ssh/proxy/http.rb +18 -20
  51. data/lib/net/ssh/proxy/https.rb +8 -10
  52. data/lib/net/ssh/proxy/jump.rb +8 -10
  53. data/lib/net/ssh/proxy/socks4.rb +2 -4
  54. data/lib/net/ssh/proxy/socks5.rb +3 -5
  55. data/lib/net/ssh/service/forward.rb +7 -7
  56. data/lib/net/ssh/test/channel.rb +23 -25
  57. data/lib/net/ssh/test/extensions.rb +35 -35
  58. data/lib/net/ssh/test/kex.rb +6 -8
  59. data/lib/net/ssh/test/local_packet.rb +0 -2
  60. data/lib/net/ssh/test/packet.rb +3 -3
  61. data/lib/net/ssh/test/remote_packet.rb +5 -7
  62. data/lib/net/ssh/test/script.rb +24 -26
  63. data/lib/net/ssh/test/socket.rb +12 -15
  64. data/lib/net/ssh/test.rb +4 -5
  65. data/lib/net/ssh/transport/algorithms.rb +14 -13
  66. data/lib/net/ssh/transport/cipher_factory.rb +28 -28
  67. data/lib/net/ssh/transport/constants.rb +3 -3
  68. data/lib/net/ssh/transport/ctr.rb +7 -7
  69. data/lib/net/ssh/transport/hmac/abstract.rb +4 -5
  70. data/lib/net/ssh/transport/hmac/md5.rb +0 -2
  71. data/lib/net/ssh/transport/hmac/md5_96.rb +0 -2
  72. data/lib/net/ssh/transport/hmac/none.rb +0 -2
  73. data/lib/net/ssh/transport/hmac/ripemd160.rb +0 -2
  74. data/lib/net/ssh/transport/hmac/sha1.rb +0 -2
  75. data/lib/net/ssh/transport/hmac/sha1_96.rb +0 -2
  76. data/lib/net/ssh/transport/hmac.rb +12 -12
  77. data/lib/net/ssh/transport/identity_cipher.rb +11 -13
  78. data/lib/net/ssh/transport/kex/abstract.rb +3 -3
  79. data/lib/net/ssh/transport/kex/abstract5656.rb +1 -1
  80. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +2 -1
  81. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +4 -4
  82. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
  83. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +21 -21
  84. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +1 -2
  85. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +2 -2
  86. data/lib/net/ssh/transport/kex.rb +8 -6
  87. data/lib/net/ssh/transport/key_expander.rb +7 -8
  88. data/lib/net/ssh/transport/openssl.rb +38 -22
  89. data/lib/net/ssh/transport/packet_stream.rb +2 -3
  90. data/lib/net/ssh/transport/server_version.rb +17 -16
  91. data/lib/net/ssh/transport/session.rb +9 -7
  92. data/lib/net/ssh/transport/state.rb +43 -43
  93. data/lib/net/ssh/verifiers/accept_new.rb +0 -2
  94. data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +1 -2
  95. data/lib/net/ssh/verifiers/always.rb +6 -4
  96. data/lib/net/ssh/verifiers/never.rb +0 -2
  97. data/lib/net/ssh/version.rb +3 -3
  98. data/lib/net/ssh.rb +4 -5
  99. data/net-ssh-public_cert.pem +8 -8
  100. data/net-ssh.gemspec +2 -2
  101. data/support/ssh_tunnel_bug.rb +3 -3
  102. data.tar.gz.sig +0 -0
  103. metadata +26 -18
  104. metadata.gz.sig +0 -0
  105. data/.travis.yml +0 -52
data/CHANGES.txt CHANGED
@@ -1,3 +1,9 @@
1
+ === 6.3.0 beta1
2
+
3
+ * Support cert based host key auth, fix asterisk in known_hosts [#833]
4
+ * Support kex dh-group14-sha256 [#795]
5
+ * Fix StrictHostKeyChecking ssh config parameter translation [#765]
6
+
1
7
  === 6.2.0 rc1
2
8
 
3
9
  === 6.2.0 beta1
@@ -43,7 +49,7 @@
43
49
  === 5.2.0.rc3
44
50
 
45
51
  * Fix check_host_ip read from config
46
- * Support ssh-ed25519 in kown hosts
52
+ * Support ssh-ed25519 in known hosts
47
53
 
48
54
  === 5.2.0.rc2
49
55
 
data/Dockerfile ADDED
@@ -0,0 +1,27 @@
1
+ ARG RUBY_VERSION=3.1
2
+ FROM ruby:${RUBY_VERSION}
3
+
4
+ RUN apt update && apt install -y openssh-server sudo netcat \
5
+ && useradd --create-home --shell '/bin/bash' --comment 'NetSSH' 'net_ssh_1' \
6
+ && useradd --create-home --shell '/bin/bash' --comment 'NetSSH' 'net_ssh_2' \
7
+ && echo net_ssh_1:foopwd | chpasswd \
8
+ && echo net_ssh_2:foo2pwd | chpasswd \
9
+ && mkdir -p /home/net_ssh_1/.ssh \
10
+ && mkdir -p /home/net_ssh_2/.ssh \
11
+ && echo "net_ssh_1 ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers \
12
+ && echo "net_ssh_2 ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers \
13
+ && ssh-keygen -f /etc/ssh/users_ca -N ''
14
+
15
+ ENV INSTALL_PATH="/netssh"
16
+
17
+ WORKDIR $INSTALL_PATH
18
+
19
+ COPY Gemfile net-ssh.gemspec $INSTALL_PATH/
20
+
21
+ COPY lib/net/ssh/version.rb $INSTALL_PATH/lib/net/ssh/version.rb
22
+
23
+ RUN gem install bundler && bundle install
24
+
25
+ COPY . $INSTALL_PATH/
26
+
27
+ CMD service ssh start && rake test && NET_SSH_NO_ED25519=1 rake test
@@ -0,0 +1,17 @@
1
+ FROM ubuntu:22.04
2
+
3
+ ENV INSTALL_PATH="/netssh"
4
+
5
+ RUN apt update && apt install -y openssl ruby ruby-dev git build-essential
6
+
7
+ WORKDIR $INSTALL_PATH
8
+
9
+ COPY Gemfile net-ssh.gemspec $INSTALL_PATH/
10
+
11
+ COPY lib/net/ssh/version.rb $INSTALL_PATH/lib/net/ssh/version.rb
12
+
13
+ RUN ls -l && gem install bundler && bundle install
14
+
15
+ COPY . $INSTALL_PATH/
16
+
17
+ CMD openssl version && ruby -ropenssl -e 'puts OpenSSL::OPENSSL_VERSION' && rake test
data/Gemfile CHANGED
@@ -9,3 +9,5 @@ if ENV["CI"]
9
9
  gem 'codecov', require: false, group: :test
10
10
  gem 'simplecov', require: false, group: :test
11
11
  end
12
+
13
+ gem 'webrick', group: %i[development test] if RUBY_VERSION.split(".")[0].to_i >= 3
data/Gemfile.noed25519 CHANGED
@@ -8,3 +8,5 @@ if ENV["CI"] && !Gem.win_platform?
8
8
  gem 'simplecov', require: false, group: :test
9
9
  gem 'codecov', require: false, group: :test
10
10
  end
11
+
12
+ gem 'webrick', group: %i[development test] if RUBY_VERSION.split(".")[0].to_i >= 3
data/README.md CHANGED
@@ -1,13 +1,13 @@
1
1
  [![Gem Version](https://badge.fury.io/rb/net-ssh.svg)](https://badge.fury.io/rb/net-ssh)
2
2
  [![Join the chat at https://gitter.im/net-ssh/net-ssh](https://badges.gitter.im/net-ssh/net-ssh.svg)](https://gitter.im/net-ssh/net-ssh?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
3
- [![Build Status](https://travis-ci.org/net-ssh/net-ssh.svg?branch=master)](https://travis-ci.org/net-ssh/net-ssh)
3
+ [![Build status](https://github.com/net-ssh/net-ssh/actions/workflows/ci.yml/badge.svg)](https://github.com/net-ssh/net-ssh/actions/workflows/ci.yml)
4
4
  [![Coverage status](https://codecov.io/gh/net-ssh/net-ssh/branch/master/graph/badge.svg)](https://codecov.io/gh/net-ssh/net-ssh)
5
5
  [![Backers on Open Collective](https://opencollective.com/net-ssh/backers/badge.svg)](#backers])
6
6
  [![Sponsors on Open Collective](https://opencollective.com/net-ssh/sponsors/badge.svg)](#sponsors)
7
7
 
8
8
  # Net::SSH 6.x
9
9
 
10
- * Docs: http://net-ssh.github.com/net-ssh
10
+ * Docs: http://net-ssh.github.io/net-ssh
11
11
  * Issues: https://github.com/net-ssh/net-ssh/issues
12
12
  * Codes: https://github.com/net-ssh/net-ssh
13
13
  * Email: net-ssh@solutious.com
@@ -211,13 +211,19 @@ Run the test suite from the net-ssh directory with the following command:
211
211
  bundle exec rake test
212
212
  ```
213
213
 
214
+ NOTE : you can run test on all ruby versions with docker :
215
+
216
+ ```
217
+ docker-compose up --build
218
+ ```
219
+
214
220
  Run a single test file like this:
215
221
 
216
222
  ```sh
217
223
  ruby -Ilib -Itest test/transport/test_server_version.rb
218
224
  ```
219
225
 
220
- To run integration tests see test/integration/README.txt
226
+ To run integration tests see [here](test/integration/README.md)
221
227
 
222
228
  ### BUILDING GEM
223
229
 
data/Rakefile CHANGED
@@ -48,6 +48,7 @@ namespace :cert do
48
48
  raw = File.read "net-ssh-public_cert.pem"
49
49
  certificate = OpenSSL::X509::Certificate.new raw
50
50
  raise Exception, "Not yet expired: #{certificate.not_after}" unless certificate.not_after < Time.now
51
+
51
52
  sh "gem cert --build netssh@solutious.com --days 365*5 --private-key /mnt/gem/net-ssh-private_key.pem"
52
53
  sh "mv gem-public_cert.pem net-ssh-public_cert.pem"
53
54
  sh "gem cert --add net-ssh-public_cert.pem"
@@ -94,6 +95,10 @@ Rake::TestTask.new do |t|
94
95
  t.test_files = test_files
95
96
  end
96
97
 
98
+ # We need to enable the OpenSSL 3.0 legacy providers for our test suite
99
+ require 'openssl'
100
+ ENV['OPENSSL_CONF'] = 'test/openssl3.conf' if OpenSSL::OPENSSL_LIBRARY_VERSION.start_with? "OpenSSL 3"
101
+
97
102
  desc "Run tests of Net::SSH:Test"
98
103
  Rake::TestTask.new do |t|
99
104
  t.name = "test_test"
@@ -0,0 +1,23 @@
1
+ version: '3'
2
+
3
+ services:
4
+ ruby-3.1:
5
+ build:
6
+ context: .
7
+ args:
8
+ RUBY_VERSION: 3.1
9
+ ruby-3.0:
10
+ build:
11
+ context: .
12
+ args:
13
+ RUBY_VERSION: 3.0
14
+ ruby-2.7:
15
+ build:
16
+ context: .
17
+ args:
18
+ RUBY_VERSION: 2.7
19
+ ruby-2.6:
20
+ build:
21
+ context: .
22
+ args:
23
+ RUBY_VERSION: 2.6
@@ -13,6 +13,7 @@ module Net
13
13
  module Authentication
14
14
  # Class for representing agent-specific errors.
15
15
  class AgentError < Net::SSH::Exception; end
16
+
16
17
  # An exception for indicating that the SSH agent is not available.
17
18
  class AgentNotAvailable < AgentError; end
18
19
 
@@ -64,7 +65,7 @@ module Net
64
65
 
65
66
  # Instantiates a new agent object, connects to a running SSH agent,
66
67
  # negotiates the agent protocol version, and returns the agent object.
67
- def self.connect(logger=nil, agent_socket_factory = nil, identity_agent = nil)
68
+ def self.connect(logger = nil, agent_socket_factory = nil, identity_agent = nil)
68
69
  agent = new(logger)
69
70
  agent.connect!(agent_socket_factory, identity_agent)
70
71
  agent.negotiate!
@@ -73,7 +74,7 @@ module Net
73
74
 
74
75
  # Creates a new Agent object, using the optional logger instance to
75
76
  # report status.
76
- def initialize(logger=nil)
77
+ def initialize(logger = nil)
77
78
  self.logger = logger
78
79
  end
79
80
 
@@ -87,9 +88,9 @@ module Net
87
88
  if agent_socket_factory
88
89
  agent_socket_factory.call
89
90
  elsif identity_agent
90
- unix_socket_class.open(identity_agent)
91
+ unix_socket_class.open(File.expand_path(identity_agent))
91
92
  elsif ENV['SSH_AUTH_SOCK'] && unix_socket_class
92
- unix_socket_class.open(ENV['SSH_AUTH_SOCK'])
93
+ unix_socket_class.open(File.expand_path(ENV['SSH_AUTH_SOCK']))
93
94
  elsif Gem.win_platform? && RUBY_ENGINE != "jruby"
94
95
  Pageant::Socket.open
95
96
  else
@@ -107,6 +108,7 @@ module Net
107
108
  type, body = send_and_wait(SSH2_AGENT_REQUEST_VERSION, :string, Transport::ServerVersion::PROTO_VERSION)
108
109
 
109
110
  raise AgentNotAvailable, "SSH2 agents are not yet supported" if type == SSH2_AGENT_VERSION_RESPONSE
111
+
110
112
  if type == SSH2_AGENT_FAILURE
111
113
  debug { "Unexpected response type==#{type}, this will be ignored" }
112
114
  elsif type != SSH_AGENT_RSA_IDENTITIES_ANSWER1 && type != SSH_AGENT_RSA_IDENTITIES_ANSWER2
@@ -175,7 +177,7 @@ module Net
175
177
 
176
178
  req_type = constraints.empty? ? SSH2_AGENT_ADD_IDENTITY : SSH2_AGENT_ADD_ID_CONSTRAINED
177
179
  type, = send_and_wait(req_type, :string, priv_key.ssh_type, :raw, blob_for_add(priv_key),
178
- :string, comment, :raw, constraints)
180
+ :string, comment, :raw, constraints)
179
181
  raise AgentError, "could not add identity to agent" if type != SSH_AGENT_SUCCESS
180
182
  end
181
183
 
@@ -196,13 +198,13 @@ module Net
196
198
  type, = send_and_wait(SSH2_AGENT_LOCK, :string, password)
197
199
  raise AgentError, "could not lock agent" if type != SSH_AGENT_SUCCESS
198
200
  end
199
-
201
+
200
202
  # unlock the ssh agent with password
201
203
  def unlock(password)
202
204
  type, = send_and_wait(SSH2_AGENT_UNLOCK, :string, password)
203
205
  raise AgentError, "could not unlock agent" if type != SSH_AGENT_SUCCESS
204
206
  end
205
-
207
+
206
208
  private
207
209
 
208
210
  def unix_socket_class
@@ -249,31 +251,31 @@ module Net
249
251
  case priv_key.ssh_type
250
252
  when /^ssh-dss$/
251
253
  Net::SSH::Buffer.from(:bignum, priv_key.p, :bignum, priv_key.q, :bignum, priv_key.g,
252
- :bignum, priv_key.pub_key, :bignum, priv_key.priv_key).to_s
254
+ :bignum, priv_key.pub_key, :bignum, priv_key.priv_key).to_s
253
255
  when /^ssh-dss-cert-v01@openssh\.com$/
254
256
  Net::SSH::Buffer.from(:string, priv_key.to_blob, :bignum, priv_key.key.priv_key).to_s
255
257
  when /^ecdsa\-sha2\-(\w*)$/
256
258
  curve_name = OpenSSL::PKey::EC::CurveNameAliasInv[priv_key.group.curve_name]
257
259
  Net::SSH::Buffer.from(:string, curve_name, :mstring, priv_key.public_key.to_bn.to_s(2),
258
- :bignum, priv_key.private_key).to_s
260
+ :bignum, priv_key.private_key).to_s
259
261
  when /^ecdsa\-sha2\-(\w*)-cert-v01@openssh\.com$/
260
262
  Net::SSH::Buffer.from(:string, priv_key.to_blob, :bignum, priv_key.key.private_key).to_s
261
263
  when /^ssh-ed25519$/
262
264
  Net::SSH::Buffer.from(:string, priv_key.public_key.verify_key.to_bytes,
263
- :string, priv_key.sign_key.keypair).to_s
265
+ :string, priv_key.sign_key.keypair).to_s
264
266
  when /^ssh-ed25519-cert-v01@openssh\.com$/
265
267
  # Unlike the other certificate types, the public key is included after the certifiate.
266
268
  Net::SSH::Buffer.from(:string, priv_key.to_blob,
267
- :string, priv_key.key.public_key.verify_key.to_bytes,
268
- :string, priv_key.key.sign_key.keypair).to_s
269
+ :string, priv_key.key.public_key.verify_key.to_bytes,
270
+ :string, priv_key.key.sign_key.keypair).to_s
269
271
  when /^ssh-rsa$/
270
272
  # `n` and `e` are reversed compared to the ordering in `OpenSSL::PKey::RSA#to_blob`.
271
273
  Net::SSH::Buffer.from(:bignum, priv_key.n, :bignum, priv_key.e, :bignum, priv_key.d,
272
- :bignum, priv_key.iqmp, :bignum, priv_key.p, :bignum, priv_key.q).to_s
274
+ :bignum, priv_key.iqmp, :bignum, priv_key.p, :bignum, priv_key.q).to_s
273
275
  when /^ssh-rsa-cert-v01@openssh\.com$/
274
276
  Net::SSH::Buffer.from(:string, priv_key.to_blob, :bignum, priv_key.key.d,
275
- :bignum, priv_key.key.iqmp, :bignum, priv_key.key.p,
276
- :bignum, priv_key.key.q).to_s
277
+ :bignum, priv_key.key.iqmp, :bignum, priv_key.key.p,
278
+ :bignum, priv_key.key.q).to_s
277
279
  end
278
280
  end
279
281
  end
@@ -31,7 +31,7 @@ module Net
31
31
  cert.key_id = buffer.read_string
32
32
  cert.valid_principals = buffer.read_buffer.read_all(&:read_string)
33
33
  cert.valid_after = Time.at(buffer.read_int64)
34
-
34
+
35
35
  cert.valid_before = if RUBY_PLATFORM == "java"
36
36
  # 0x20c49ba5e353f7 = 0x7fffffffffffffff/1000, the largest value possible for JRuby
37
37
  # JRuby Time.at multiplies the arg by 1000, and then stores it in a signed long.
@@ -66,8 +66,8 @@ module Net
66
66
  ).to_s
67
67
  end
68
68
 
69
- def ssh_do_sign(data)
70
- key.ssh_do_sign(data)
69
+ def ssh_do_sign(data, sig_alg = nil)
70
+ key.ssh_do_sign(data, sig_alg)
71
71
  end
72
72
 
73
73
  def ssh_do_verify(sig, data, options = {})
@@ -83,7 +83,7 @@ module Net
83
83
  end
84
84
 
85
85
  # Signs the certificate with key.
86
- def sign!(key, sign_nonce=nil)
86
+ def sign!(key, sign_nonce = nil)
87
87
  # ssh-keygen uses 32 bytes of nonce.
88
88
  self.nonce = sign_nonce || SecureRandom.random_bytes(32)
89
89
  self.signature_key = key
@@ -94,7 +94,7 @@ module Net
94
94
  self
95
95
  end
96
96
 
97
- def sign(key, sign_nonce=nil)
97
+ def sign(key, sign_nonce = nil)
98
98
  cert = clone
99
99
  cert.sign!(key, sign_nonce)
100
100
  end
@@ -125,6 +125,7 @@ module Net
125
125
  def self.type_symbol(type)
126
126
  types = { 1 => :user, 2 => :host }
127
127
  raise ArgumentError("unsupported type: #{type}") unless types.include?(type)
128
+
128
129
  types.fetch(type)
129
130
  end
130
131
  private_class_method :type_symbol
@@ -134,6 +135,7 @@ module Net
134
135
  def type_value(type)
135
136
  types = { user: 1, host: 2 }
136
137
  raise ArgumentError("unsupported type: #{type}") unless types.include?(type)
138
+
137
139
  types.fetch(type)
138
140
  end
139
141
 
@@ -1,7 +1,6 @@
1
1
  module Net
2
2
  module SSH
3
3
  module Authentication
4
-
5
4
  # Describes the constants used by the Net::SSH::Authentication components
6
5
  # of the Net::SSH library. Individual authentication method implemenations
7
6
  # may define yet more constants that are specific to their implementation.
@@ -14,7 +14,7 @@ module Net
14
14
  module Authentication
15
15
  module ED25519
16
16
  class SigningKeyFromFile < SimpleDelegator
17
- def initialize(pk,sk)
17
+ def initialize(pk, sk)
18
18
  key = ::Ed25519::SigningKey.from_keypair(sk)
19
19
  raise ArgumentError, "pk does not match sk" unless pk == key.verify_key.to_bytes
20
20
 
@@ -44,9 +44,11 @@ module Net
44
44
  datafull = datafull.strip
45
45
  raise ArgumentError.new("Expected #{MBEGIN} at start of private key") unless datafull.start_with?(MBEGIN)
46
46
  raise ArgumentError.new("Expected #{MEND} at end of private key") unless datafull.end_with?(MEND)
47
+
47
48
  datab64 = datafull[MBEGIN.size...-MEND.size]
48
49
  data = Base64.decode64(datab64)
49
50
  raise ArgumentError.new("Expected #{MAGIC} at start of decoded private key") unless data.start_with?(MAGIC)
51
+
50
52
  buffer = Net::SSH::Buffer.new(data[MAGIC.size + 1..-1])
51
53
 
52
54
  ciphername = buffer.read_string
@@ -59,6 +61,7 @@ module Net
59
61
  kdfopts = Net::SSH::Buffer.new(buffer.read_string)
60
62
  num_keys = buffer.read_long
61
63
  raise ArgumentError.new("Only 1 key is supported in ssh keys #{num_keys} was in private key") unless num_keys == 1
64
+
62
65
  _pubkey = buffer.read_string
63
66
 
64
67
  len = buffer.read_long
@@ -72,12 +75,13 @@ module Net
72
75
  rounds = kdfopts.read_long
73
76
 
74
77
  raise "BCryptPbkdf is not implemented for jruby" if RUBY_PLATFORM == "java"
78
+
75
79
  key = BCryptPbkdf::key(password, salt, keylen + ivlen, rounds)
76
80
  else
77
81
  key = '\x00' * (keylen + ivlen)
78
82
  end
79
83
 
80
- cipher = CipherFactory.get(ciphername, key: key[0...keylen], iv:key[keylen...keylen + ivlen], decrypt: true)
84
+ cipher = CipherFactory.get(ciphername, key: key[0...keylen], iv: key[keylen...keylen + ivlen], decrypt: true)
81
85
 
82
86
  decoded = cipher.update(buffer.remainder_as_buffer.to_s)
83
87
  decoded << cipher.final
@@ -112,7 +116,7 @@ module Net
112
116
  end
113
117
 
114
118
  def to_blob
115
- Net::SSH::Buffer.from(:mstring,"ssh-ed25519",:string,@verify_key.to_bytes).to_s
119
+ Net::SSH::Buffer.from(:mstring, "ssh-ed25519".dup, :string, @verify_key.to_bytes).to_s
116
120
  end
117
121
 
118
122
  def ssh_type
@@ -124,7 +128,7 @@ module Net
124
128
  end
125
129
 
126
130
  def ssh_do_verify(sig, data, options = {})
127
- @verify_key.verify(sig,data)
131
+ @verify_key.verify(sig, data)
128
132
  end
129
133
 
130
134
  def to_pem
@@ -148,7 +152,7 @@ module Net
148
152
  _comment = buffer.read_string
149
153
 
150
154
  @pk = pk
151
- @sign_key = SigningKeyFromFile.new(pk,sk)
155
+ @sign_key = SigningKeyFromFile.new(pk, sk)
152
156
  end
153
157
 
154
158
  def to_blob
@@ -167,7 +171,7 @@ module Net
167
171
  PubKey.new(@pk)
168
172
  end
169
173
 
170
- def ssh_do_sign(data)
174
+ def ssh_do_sign(data, sig_alg = nil)
171
175
  @sign_key.sign(data)
172
176
  end
173
177
 
@@ -1,11 +1,9 @@
1
- module Net
2
- module SSH
1
+ module Net
2
+ module SSH
3
3
  module Authentication
4
-
5
4
  # Loads ED25519 support which requires optinal dependecies like
6
5
  # ed25519, bcrypt_pbkdf
7
6
  module ED25519Loader
8
-
9
7
  begin
10
8
  require 'net/ssh/authentication/ed25519'
11
9
  LOADED = true
@@ -14,20 +12,19 @@ module Net
14
12
  ERROR = e
15
13
  LOADED = false
16
14
  end
17
-
15
+
18
16
  def self.raiseUnlessLoaded(message)
19
17
  description = ERROR.is_a?(LoadError) ? dependenciesRequiredForED25519 : ''
20
18
  description << "#{ERROR.class} : \"#{ERROR.message}\"\n" if ERROR
21
19
  raise NotImplementedError, "#{message}\n#{description}" unless LOADED
22
20
  end
23
-
21
+
24
22
  def self.dependenciesRequiredForED25519
25
23
  result = "net-ssh requires the following gems for ed25519 support:\n"
26
24
  result << " * ed25519 (>= 1.2, < 2.0)\n"
27
25
  result << " * bcrypt_pbkdf (>= 1.0, < 2.0)\n" unless RUBY_PLATFORM == "java"
28
26
  result << "See https://github.com/net-ssh/net-ssh/issues/565 for more information\n"
29
27
  end
30
-
31
28
  end
32
29
  end
33
30
  end
@@ -6,7 +6,6 @@ require 'net/ssh/authentication/agent'
6
6
  module Net
7
7
  module SSH
8
8
  module Authentication
9
-
10
9
  # A trivial exception class used to report errors in the key manager.
11
10
  class KeyManagerError < Net::SSH::Exception; end
12
11
 
@@ -42,7 +41,7 @@ module Net
42
41
  # Create a new KeyManager. By default, the manager will
43
42
  # use the ssh-agent if it is running and the `:use_agent` option
44
43
  # is not false.
45
- def initialize(logger, options={})
44
+ def initialize(logger, options = {})
46
45
  self.logger = logger
47
46
  @key_files = []
48
47
  @key_data = []
@@ -159,7 +158,7 @@ module Net
159
158
  # Regardless of the identity's origin or who does the signing, this
160
159
  # will always return the signature in an SSH2-specified "signature
161
160
  # blob" format.
162
- def sign(identity, data)
161
+ def sign(identity, data, sig_alg = nil)
163
162
  info = known_identities[identity] or raise KeyManagerError, "the given identity is unknown to the key manager"
164
163
 
165
164
  if info[:key].nil? && info[:from] == :file
@@ -171,13 +170,27 @@ module Net
171
170
  end
172
171
 
173
172
  if info[:key]
174
- return Net::SSH::Buffer.from(:string, identity.ssh_signature_type,
175
- :mstring, info[:key].ssh_do_sign(data.to_s)).to_s
173
+ if sig_alg.nil?
174
+ signed = info[:key].ssh_do_sign(data.to_s)
175
+ sig_alg = identity.ssh_signature_type
176
+ else
177
+ signed = info[:key].ssh_do_sign(data.to_s, sig_alg)
178
+ end
179
+ return Net::SSH::Buffer.from(:string, sig_alg,
180
+ :mstring, signed).to_s
176
181
  end
177
182
 
178
183
  if info[:from] == :agent
179
184
  raise KeyManagerError, "the agent is no longer available" unless agent
180
- return agent.sign(info[:identity], data.to_s)
185
+
186
+ case sig_alg
187
+ when "rsa-sha2-512"
188
+ return agent.sign(info[:identity], data.to_s, Net::SSH::Authentication::Agent::SSH_AGENT_RSA_SHA2_512)
189
+ when "rsa-sha2-256"
190
+ return agent.sign(info[:identity], data.to_s, Net::SSH::Authentication::Agent::SSH_AGENT_RSA_SHA2_256)
191
+ else
192
+ return agent.sign(info[:identity], data.to_s)
193
+ end
181
194
  end
182
195
 
183
196
  raise KeyManagerError, "[BUG] can't determine identity origin (#{info.inspect})"
@@ -201,6 +214,7 @@ module Net
201
214
  # or if the agent is otherwise not available.
202
215
  def agent
203
216
  return unless use_agent?
217
+
204
218
  @agent ||= Agent.connect(logger, options[:agent_socket_factory], options[:identity_agent])
205
219
  rescue AgentNotAvailable
206
220
  @use_agent = false
@@ -248,37 +262,35 @@ module Net
248
262
  # Load prepared identities. Private key decryption errors ignored if ignore_decryption_errors
249
263
  def load_identities(identities, ask_passphrase, ignore_decryption_errors)
250
264
  identities.map do |identity|
251
- begin
252
- case identity[:load_from]
253
- when :pubkey_file
254
- key = KeyFactory.load_public_key(identity[:pubkey_file])
255
- { public_key: key, from: :file, file: identity[:privkey_file] }
256
- when :privkey_file
257
- private_key = KeyFactory.load_private_key(
258
- identity[:privkey_file], options[:passphrase], ask_passphrase, options[:password_prompt]
259
- )
260
- key = private_key.send(:public_key)
261
- { public_key: key, from: :file, file: identity[:privkey_file], key: private_key }
262
- when :data
263
- private_key = KeyFactory.load_data_private_key(
264
- identity[:data], options[:passphrase], ask_passphrase, "<key in memory>", options[:password_prompt]
265
- )
266
- key = private_key.send(:public_key)
267
- { public_key: key, from: :key_data, data: identity[:data], key: private_key }
268
- else
269
- identity
270
- end
271
- rescue OpenSSL::PKey::RSAError, OpenSSL::PKey::DSAError, OpenSSL::PKey::ECError, OpenSSL::PKey::PKeyError, ArgumentError => e
272
- if ignore_decryption_errors
273
- identity
274
- else
275
- process_identity_loading_error(identity, e)
276
- nil
277
- end
278
- rescue Exception => e
265
+ case identity[:load_from]
266
+ when :pubkey_file
267
+ key = KeyFactory.load_public_key(identity[:pubkey_file])
268
+ { public_key: key, from: :file, file: identity[:privkey_file] }
269
+ when :privkey_file
270
+ private_key = KeyFactory.load_private_key(
271
+ identity[:privkey_file], options[:passphrase], ask_passphrase, options[:password_prompt]
272
+ )
273
+ key = private_key.send(:public_key)
274
+ { public_key: key, from: :file, file: identity[:privkey_file], key: private_key }
275
+ when :data
276
+ private_key = KeyFactory.load_data_private_key(
277
+ identity[:data], options[:passphrase], ask_passphrase, "<key in memory>", options[:password_prompt]
278
+ )
279
+ key = private_key.send(:public_key)
280
+ { public_key: key, from: :key_data, data: identity[:data], key: private_key }
281
+ else
282
+ identity
283
+ end
284
+ rescue OpenSSL::PKey::RSAError, OpenSSL::PKey::DSAError, OpenSSL::PKey::ECError, OpenSSL::PKey::PKeyError, ArgumentError => e
285
+ if ignore_decryption_errors
286
+ identity
287
+ else
279
288
  process_identity_loading_error(identity, e)
280
289
  nil
281
290
  end
291
+ rescue Exception => e
292
+ process_identity_loading_error(identity, e)
293
+ nil
282
294
  end.compact
283
295
  end
284
296
 
@@ -7,7 +7,6 @@ module Net
7
7
  module SSH
8
8
  module Authentication
9
9
  module Methods
10
-
11
10
  # The base class of all user authentication methods. It provides a few
12
11
  # bits of common functionality.
13
12
  class Abstract
@@ -21,12 +20,22 @@ module Net
21
20
  # this.
22
21
  attr_reader :key_manager
23
22
 
23
+ # So far only affects algorithms used for rsa keys, but can be
24
+ # extended to other keys, e.g after reading of
25
+ # PubkeyAcceptedAlgorithms option from ssh_config file is implemented.
26
+ attr_reader :pubkey_algorithms
27
+
24
28
  # Instantiates a new authentication method.
25
- def initialize(session, options={})
29
+ def initialize(session, options = {})
26
30
  @session = session
27
31
  @key_manager = options[:key_manager]
28
32
  @options = options
29
33
  @prompt = options[:password_prompt]
34
+ @pubkey_algorithms = options[:pubkey_algorithms] \
35
+ || %w[rsa-sha2-256-cert-v01@openssh.com
36
+ ssh-rsa-cert-v01@openssh.com
37
+ rsa-sha2-256
38
+ ssh-rsa]
30
39
  self.logger = session.logger
31
40
  end
32
41
 
@@ -47,7 +56,7 @@ module Net
47
56
  # of the packet. The new packet is returned, ready for sending.
48
57
  def userauth_request(username, next_service, auth_method, *others)
49
58
  buffer = Net::SSH::Buffer.from(:byte, USERAUTH_REQUEST,
50
- :string, username, :string, next_service, :string, auth_method)
59
+ :string, username, :string, next_service, :string, auth_method)
51
60
 
52
61
  others.each do |value|
53
62
  case value
@@ -4,19 +4,18 @@ module Net
4
4
  module SSH
5
5
  module Authentication
6
6
  module Methods
7
-
8
7
  # Implements the host-based SSH authentication method.
9
8
  class Hostbased < Abstract
10
9
  include Constants
11
10
 
12
11
  # Attempts to perform host-based authorization of the user by trying
13
12
  # all known keys.
14
- def authenticate(next_service, username, password=nil)
13
+ def authenticate(next_service, username, password = nil)
15
14
  return false unless key_manager
16
15
 
17
16
  key_manager.each_identity do |identity|
18
17
  return true if authenticate_with(identity, next_service,
19
- username, key_manager)
18
+ username, key_manager)
20
19
  end
21
20
 
22
21
  return false
@@ -64,10 +63,9 @@ module Net
64
63
  # Build the "core" hostbased request string.
65
64
  def build_request(identity, next_service, username, hostname, client_username)
66
65
  userauth_request(username, next_service, "hostbased", identity.ssh_type,
67
- Buffer.from(:key, identity).to_s, hostname, client_username).to_s
66
+ Buffer.from(:key, identity).to_s, hostname, client_username).to_s
68
67
  end
69
68
  end
70
-
71
69
  end
72
70
  end
73
71
  end