net-ssh 6.2.0.rc2 → 7.0.1
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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.dockerignore +6 -0
- data/.github/config/rubocop_linter_action.yml +4 -0
- data/.github/workflows/ci-with-docker.yml +44 -0
- data/.github/workflows/ci.yml +22 -11
- data/.github/workflows/rubocop.yml +13 -0
- data/.rubocop.yml +12 -1
- data/.rubocop_todo.yml +470 -262
- data/CHANGES.txt +6 -0
- data/Dockerfile +27 -0
- data/Dockerfile.openssl3 +17 -0
- data/Gemfile +2 -0
- data/Gemfile.noed25519 +2 -0
- data/README.md +9 -3
- data/Rakefile +5 -0
- data/docker-compose.yml +23 -0
- data/lib/net/ssh/authentication/agent.rb +17 -15
- data/lib/net/ssh/authentication/certificate.rb +7 -5
- data/lib/net/ssh/authentication/constants.rb +0 -1
- data/lib/net/ssh/authentication/ed25519.rb +10 -6
- data/lib/net/ssh/authentication/ed25519_loader.rb +4 -7
- data/lib/net/ssh/authentication/key_manager.rb +46 -34
- data/lib/net/ssh/authentication/methods/abstract.rb +12 -3
- data/lib/net/ssh/authentication/methods/hostbased.rb +3 -5
- data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +2 -2
- data/lib/net/ssh/authentication/methods/none.rb +6 -9
- data/lib/net/ssh/authentication/methods/password.rb +2 -3
- data/lib/net/ssh/authentication/methods/publickey.rb +56 -16
- data/lib/net/ssh/authentication/pageant.rb +97 -97
- data/lib/net/ssh/authentication/pub_key_fingerprint.rb +2 -2
- data/lib/net/ssh/authentication/session.rb +18 -17
- data/lib/net/ssh/buffer.rb +50 -30
- data/lib/net/ssh/buffered_io.rb +24 -25
- data/lib/net/ssh/config.rb +33 -20
- data/lib/net/ssh/connection/channel.rb +84 -83
- data/lib/net/ssh/connection/constants.rb +0 -4
- data/lib/net/ssh/connection/event_loop.rb +30 -24
- data/lib/net/ssh/connection/keepalive.rb +12 -12
- data/lib/net/ssh/connection/session.rb +108 -107
- data/lib/net/ssh/connection/term.rb +56 -58
- data/lib/net/ssh/errors.rb +12 -12
- data/lib/net/ssh/key_factory.rb +7 -8
- data/lib/net/ssh/known_hosts.rb +84 -15
- data/lib/net/ssh/loggable.rb +8 -9
- data/lib/net/ssh/packet.rb +1 -1
- data/lib/net/ssh/prompt.rb +9 -11
- data/lib/net/ssh/proxy/command.rb +1 -1
- data/lib/net/ssh/proxy/errors.rb +2 -4
- data/lib/net/ssh/proxy/http.rb +18 -20
- data/lib/net/ssh/proxy/https.rb +8 -10
- data/lib/net/ssh/proxy/jump.rb +8 -10
- data/lib/net/ssh/proxy/socks4.rb +2 -4
- data/lib/net/ssh/proxy/socks5.rb +3 -5
- data/lib/net/ssh/service/forward.rb +7 -7
- data/lib/net/ssh/test/channel.rb +23 -25
- data/lib/net/ssh/test/extensions.rb +35 -35
- data/lib/net/ssh/test/kex.rb +6 -8
- data/lib/net/ssh/test/local_packet.rb +0 -2
- data/lib/net/ssh/test/packet.rb +3 -3
- data/lib/net/ssh/test/remote_packet.rb +5 -7
- data/lib/net/ssh/test/script.rb +24 -26
- data/lib/net/ssh/test/socket.rb +12 -15
- data/lib/net/ssh/test.rb +4 -5
- data/lib/net/ssh/transport/algorithms.rb +14 -13
- data/lib/net/ssh/transport/cipher_factory.rb +28 -28
- data/lib/net/ssh/transport/constants.rb +3 -3
- data/lib/net/ssh/transport/ctr.rb +7 -7
- data/lib/net/ssh/transport/hmac/abstract.rb +4 -5
- data/lib/net/ssh/transport/hmac/md5.rb +0 -2
- data/lib/net/ssh/transport/hmac/md5_96.rb +0 -2
- data/lib/net/ssh/transport/hmac/none.rb +0 -2
- data/lib/net/ssh/transport/hmac/ripemd160.rb +0 -2
- data/lib/net/ssh/transport/hmac/sha1.rb +0 -2
- data/lib/net/ssh/transport/hmac/sha1_96.rb +0 -2
- data/lib/net/ssh/transport/hmac.rb +12 -12
- data/lib/net/ssh/transport/identity_cipher.rb +11 -13
- data/lib/net/ssh/transport/kex/abstract.rb +3 -3
- data/lib/net/ssh/transport/kex/abstract5656.rb +1 -1
- data/lib/net/ssh/transport/kex/curve25519_sha256.rb +2 -1
- data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +4 -4
- data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
- data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +21 -21
- data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +1 -2
- data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +2 -2
- data/lib/net/ssh/transport/kex.rb +8 -6
- data/lib/net/ssh/transport/key_expander.rb +7 -8
- data/lib/net/ssh/transport/openssl.rb +38 -22
- data/lib/net/ssh/transport/packet_stream.rb +2 -3
- data/lib/net/ssh/transport/server_version.rb +17 -16
- data/lib/net/ssh/transport/session.rb +9 -7
- data/lib/net/ssh/transport/state.rb +43 -43
- data/lib/net/ssh/verifiers/accept_new.rb +0 -2
- data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +1 -2
- data/lib/net/ssh/verifiers/always.rb +6 -4
- data/lib/net/ssh/verifiers/never.rb +0 -2
- data/lib/net/ssh/version.rb +4 -4
- data/lib/net/ssh.rb +4 -5
- data/net-ssh-public_cert.pem +8 -8
- data/net-ssh.gemspec +2 -2
- data/support/ssh_tunnel_bug.rb +3 -3
- data.tar.gz.sig +0 -0
- metadata +24 -17
- metadata.gz.sig +0 -0
- data/.travis.yml +0 -52
data/CHANGES.txt
CHANGED
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
|
data/Dockerfile.openssl3
ADDED
|
@@ -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
data/Gemfile.noed25519
CHANGED
data/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
[](https://badge.fury.io/rb/net-ssh)
|
|
2
2
|
[](https://gitter.im/net-ssh/net-ssh?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
3
|
-
[](https://github.com/net-ssh/net-ssh/actions/workflows/ci.yml)
|
|
4
4
|
[](https://codecov.io/gh/net-ssh/net-ssh)
|
|
5
5
|
[](#backers])
|
|
6
6
|
[](#sponsors)
|
|
7
7
|
|
|
8
8
|
# Net::SSH 6.x
|
|
9
9
|
|
|
10
|
-
* Docs: http://net-ssh.github.
|
|
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.
|
|
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"
|
data/docker-compose.yml
ADDED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
268
|
-
|
|
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
|
-
|
|
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
|
-
|
|
276
|
-
|
|
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"
|
|
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
|
-
|
|
175
|
-
|
|
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
|
-
|
|
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
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|