net-ssh 5.2.0 → 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 +5 -5
- 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 +87 -0
- data/.github/workflows/rubocop.yml +13 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +16 -2
- data/.rubocop_todo.yml +623 -511
- data/CHANGES.txt +50 -2
- data/Dockerfile +27 -0
- data/Dockerfile.openssl3 +17 -0
- data/Gemfile +2 -0
- data/Gemfile.noed25519 +2 -0
- data/Manifest +0 -1
- data/README.md +293 -0
- data/Rakefile +6 -2
- data/appveyor.yml +4 -2
- data/docker-compose.yml +23 -0
- data/lib/net/ssh/authentication/agent.rb +29 -13
- data/lib/net/ssh/authentication/certificate.rb +19 -7
- data/lib/net/ssh/authentication/constants.rb +0 -1
- data/lib/net/ssh/authentication/ed25519.rb +13 -8
- data/lib/net/ssh/authentication/ed25519_loader.rb +5 -8
- data/lib/net/ssh/authentication/key_manager.rb +73 -32
- 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 +5 -3
- 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 -3
- data/lib/net/ssh/authentication/session.rb +27 -23
- data/lib/net/ssh/buffer.rb +51 -40
- data/lib/net/ssh/buffered_io.rb +24 -26
- data/lib/net/ssh/config.rb +82 -50
- data/lib/net/ssh/connection/channel.rb +101 -87
- data/lib/net/ssh/connection/constants.rb +0 -4
- data/lib/net/ssh/connection/event_loop.rb +30 -25
- data/lib/net/ssh/connection/keepalive.rb +12 -12
- data/lib/net/ssh/connection/session.rb +115 -111
- data/lib/net/ssh/connection/term.rb +56 -58
- data/lib/net/ssh/errors.rb +12 -12
- data/lib/net/ssh/key_factory.rb +10 -13
- data/lib/net/ssh/known_hosts.rb +106 -39
- data/lib/net/ssh/loggable.rb +10 -11
- data/lib/net/ssh/packet.rb +1 -1
- data/lib/net/ssh/prompt.rb +9 -11
- data/lib/net/ssh/proxy/command.rb +1 -2
- 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 -6
- data/lib/net/ssh/service/forward.rb +9 -8
- data/lib/net/ssh/test/channel.rb +24 -26
- 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 +6 -8
- data/lib/net/ssh/test/script.rb +25 -27
- data/lib/net/ssh/test/socket.rb +12 -15
- data/lib/net/ssh/test.rb +7 -7
- data/lib/net/ssh/transport/algorithms.rb +100 -58
- data/lib/net/ssh/transport/cipher_factory.rb +34 -50
- data/lib/net/ssh/transport/constants.rb +13 -9
- data/lib/net/ssh/transport/ctr.rb +8 -14
- data/lib/net/ssh/transport/hmac/abstract.rb +20 -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/sha2_256.rb +7 -11
- data/lib/net/ssh/transport/hmac/sha2_256_96.rb +4 -8
- data/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
- data/lib/net/ssh/transport/hmac/sha2_512.rb +6 -9
- data/lib/net/ssh/transport/hmac/sha2_512_96.rb +4 -8
- data/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
- data/lib/net/ssh/transport/hmac.rb +13 -11
- data/lib/net/ssh/transport/identity_cipher.rb +11 -13
- data/lib/net/ssh/transport/kex/abstract.rb +130 -0
- data/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
- data/lib/net/ssh/transport/kex/curve25519_sha256.rb +39 -0
- data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
- data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +5 -19
- data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
- data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +30 -139
- data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +1 -8
- data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +5 -9
- data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +20 -81
- data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +5 -4
- data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +5 -4
- data/lib/net/ssh/transport/kex.rb +15 -10
- data/lib/net/ssh/transport/key_expander.rb +7 -8
- data/lib/net/ssh/transport/openssl.rb +149 -127
- data/lib/net/ssh/transport/packet_stream.rb +50 -16
- 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 +44 -44
- 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 +3 -3
- data/lib/net/ssh.rb +12 -8
- data/net-ssh-public_cert.pem +8 -8
- data/net-ssh.gemspec +9 -7
- data/support/ssh_tunnel_bug.rb +3 -3
- data.tar.gz.sig +0 -0
- metadata +55 -30
- metadata.gz.sig +0 -0
- data/.travis.yml +0 -53
- data/Gemfile.noed25519.lock +0 -41
- data/README.rdoc +0 -194
- data/lib/net/ssh/ruby_compat.rb +0 -13
- data/support/arcfour_check.rb +0 -20
|
@@ -31,7 +31,17 @@ 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
|
+
cert.valid_before = if RUBY_PLATFORM == "java"
|
|
36
|
+
# 0x20c49ba5e353f7 = 0x7fffffffffffffff/1000, the largest value possible for JRuby
|
|
37
|
+
# JRuby Time.at multiplies the arg by 1000, and then stores it in a signed long.
|
|
38
|
+
# 0x20c49ba2d52500 = 292278993-01-01 00:00:00 +0000
|
|
39
|
+
# JRuby 9.1 does not accept the year 292278994 because of edge cases (https://github.com/JodaOrg/joda-time/issues/190)
|
|
40
|
+
Time.at([0x20c49ba2d52500, buffer.read_int64].min)
|
|
41
|
+
else
|
|
42
|
+
Time.at(buffer.read_int64)
|
|
43
|
+
end
|
|
44
|
+
|
|
35
45
|
cert.critical_options = read_options(buffer)
|
|
36
46
|
cert.extensions = read_options(buffer)
|
|
37
47
|
cert.reserved = buffer.read_string
|
|
@@ -56,12 +66,12 @@ module Net
|
|
|
56
66
|
).to_s
|
|
57
67
|
end
|
|
58
68
|
|
|
59
|
-
def ssh_do_sign(data)
|
|
60
|
-
key.ssh_do_sign(data)
|
|
69
|
+
def ssh_do_sign(data, sig_alg = nil)
|
|
70
|
+
key.ssh_do_sign(data, sig_alg)
|
|
61
71
|
end
|
|
62
72
|
|
|
63
|
-
def ssh_do_verify(sig, data)
|
|
64
|
-
key.ssh_do_verify(sig, data)
|
|
73
|
+
def ssh_do_verify(sig, data, options = {})
|
|
74
|
+
key.ssh_do_verify(sig, data, options)
|
|
65
75
|
end
|
|
66
76
|
|
|
67
77
|
def to_pem
|
|
@@ -73,7 +83,7 @@ module Net
|
|
|
73
83
|
end
|
|
74
84
|
|
|
75
85
|
# Signs the certificate with key.
|
|
76
|
-
def sign!(key, sign_nonce=nil)
|
|
86
|
+
def sign!(key, sign_nonce = nil)
|
|
77
87
|
# ssh-keygen uses 32 bytes of nonce.
|
|
78
88
|
self.nonce = sign_nonce || SecureRandom.random_bytes(32)
|
|
79
89
|
self.signature_key = key
|
|
@@ -84,7 +94,7 @@ module Net
|
|
|
84
94
|
self
|
|
85
95
|
end
|
|
86
96
|
|
|
87
|
-
def sign(key, sign_nonce=nil)
|
|
97
|
+
def sign(key, sign_nonce = nil)
|
|
88
98
|
cert = clone
|
|
89
99
|
cert.sign!(key, sign_nonce)
|
|
90
100
|
end
|
|
@@ -115,6 +125,7 @@ module Net
|
|
|
115
125
|
def self.type_symbol(type)
|
|
116
126
|
types = { 1 => :user, 2 => :host }
|
|
117
127
|
raise ArgumentError("unsupported type: #{type}") unless types.include?(type)
|
|
128
|
+
|
|
118
129
|
types.fetch(type)
|
|
119
130
|
end
|
|
120
131
|
private_class_method :type_symbol
|
|
@@ -124,6 +135,7 @@ module Net
|
|
|
124
135
|
def type_value(type)
|
|
125
136
|
types = { user: 1, host: 2 }
|
|
126
137
|
raise ArgumentError("unsupported type: #{type}") unless types.include?(type)
|
|
138
|
+
|
|
127
139
|
types.fetch(type)
|
|
128
140
|
end
|
|
129
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
|
|
|
@@ -26,7 +26,7 @@ module Net
|
|
|
26
26
|
CipherFactory = Net::SSH::Transport::CipherFactory
|
|
27
27
|
|
|
28
28
|
MBEGIN = "-----BEGIN OPENSSH PRIVATE KEY-----\n"
|
|
29
|
-
MEND = "-----END OPENSSH PRIVATE KEY
|
|
29
|
+
MEND = "-----END OPENSSH PRIVATE KEY-----"
|
|
30
30
|
MAGIC = "openssh-key-v1"
|
|
31
31
|
|
|
32
32
|
class DecryptError < ArgumentError
|
|
@@ -41,11 +41,14 @@ module Net
|
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
def self.read(datafull, password)
|
|
44
|
+
datafull = datafull.strip
|
|
44
45
|
raise ArgumentError.new("Expected #{MBEGIN} at start of private key") unless datafull.start_with?(MBEGIN)
|
|
45
46
|
raise ArgumentError.new("Expected #{MEND} at end of private key") unless datafull.end_with?(MEND)
|
|
47
|
+
|
|
46
48
|
datab64 = datafull[MBEGIN.size...-MEND.size]
|
|
47
49
|
data = Base64.decode64(datab64)
|
|
48
50
|
raise ArgumentError.new("Expected #{MAGIC} at start of decoded private key") unless data.start_with?(MAGIC)
|
|
51
|
+
|
|
49
52
|
buffer = Net::SSH::Buffer.new(data[MAGIC.size + 1..-1])
|
|
50
53
|
|
|
51
54
|
ciphername = buffer.read_string
|
|
@@ -58,6 +61,7 @@ module Net
|
|
|
58
61
|
kdfopts = Net::SSH::Buffer.new(buffer.read_string)
|
|
59
62
|
num_keys = buffer.read_long
|
|
60
63
|
raise ArgumentError.new("Only 1 key is supported in ssh keys #{num_keys} was in private key") unless num_keys == 1
|
|
64
|
+
|
|
61
65
|
_pubkey = buffer.read_string
|
|
62
66
|
|
|
63
67
|
len = buffer.read_long
|
|
@@ -71,12 +75,13 @@ module Net
|
|
|
71
75
|
rounds = kdfopts.read_long
|
|
72
76
|
|
|
73
77
|
raise "BCryptPbkdf is not implemented for jruby" if RUBY_PLATFORM == "java"
|
|
78
|
+
|
|
74
79
|
key = BCryptPbkdf::key(password, salt, keylen + ivlen, rounds)
|
|
75
80
|
else
|
|
76
81
|
key = '\x00' * (keylen + ivlen)
|
|
77
82
|
end
|
|
78
83
|
|
|
79
|
-
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)
|
|
80
85
|
|
|
81
86
|
decoded = cipher.update(buffer.remainder_as_buffer.to_s)
|
|
82
87
|
decoded << cipher.final
|
|
@@ -111,7 +116,7 @@ module Net
|
|
|
111
116
|
end
|
|
112
117
|
|
|
113
118
|
def to_blob
|
|
114
|
-
Net::SSH::Buffer.from(:mstring,"ssh-ed25519"
|
|
119
|
+
Net::SSH::Buffer.from(:mstring, "ssh-ed25519".dup, :string, @verify_key.to_bytes).to_s
|
|
115
120
|
end
|
|
116
121
|
|
|
117
122
|
def ssh_type
|
|
@@ -122,8 +127,8 @@ module Net
|
|
|
122
127
|
ssh_type
|
|
123
128
|
end
|
|
124
129
|
|
|
125
|
-
def ssh_do_verify(sig,data)
|
|
126
|
-
@verify_key.verify(sig,data)
|
|
130
|
+
def ssh_do_verify(sig, data, options = {})
|
|
131
|
+
@verify_key.verify(sig, data)
|
|
127
132
|
end
|
|
128
133
|
|
|
129
134
|
def to_pem
|
|
@@ -147,7 +152,7 @@ module Net
|
|
|
147
152
|
_comment = buffer.read_string
|
|
148
153
|
|
|
149
154
|
@pk = pk
|
|
150
|
-
@sign_key = SigningKeyFromFile.new(pk,sk)
|
|
155
|
+
@sign_key = SigningKeyFromFile.new(pk, sk)
|
|
151
156
|
end
|
|
152
157
|
|
|
153
158
|
def to_blob
|
|
@@ -166,7 +171,7 @@ module Net
|
|
|
166
171
|
PubKey.new(@pk)
|
|
167
172
|
end
|
|
168
173
|
|
|
169
|
-
def ssh_do_sign(data)
|
|
174
|
+
def ssh_do_sign(data, sig_alg = nil)
|
|
170
175
|
@sign_key.sign(data)
|
|
171
176
|
end
|
|
172
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
|
|
|
@@ -30,6 +29,9 @@ module Net
|
|
|
30
29
|
# The list of user key data that will be examined
|
|
31
30
|
attr_reader :key_data
|
|
32
31
|
|
|
32
|
+
# The list of user key certificate files that will be examined
|
|
33
|
+
attr_reader :keycert_files
|
|
34
|
+
|
|
33
35
|
# The map of loaded identities
|
|
34
36
|
attr_reader :known_identities
|
|
35
37
|
|
|
@@ -39,10 +41,11 @@ module Net
|
|
|
39
41
|
# Create a new KeyManager. By default, the manager will
|
|
40
42
|
# use the ssh-agent if it is running and the `:use_agent` option
|
|
41
43
|
# is not false.
|
|
42
|
-
def initialize(logger, options={})
|
|
44
|
+
def initialize(logger, options = {})
|
|
43
45
|
self.logger = logger
|
|
44
46
|
@key_files = []
|
|
45
47
|
@key_data = []
|
|
48
|
+
@keycert_files = []
|
|
46
49
|
@use_agent = options[:use_agent] != false
|
|
47
50
|
@known_identities = {}
|
|
48
51
|
@agent = nil
|
|
@@ -66,6 +69,12 @@ module Net
|
|
|
66
69
|
self
|
|
67
70
|
end
|
|
68
71
|
|
|
72
|
+
# Add the given keycert_file to the list of keycert files that will be used.
|
|
73
|
+
def add_keycert(keycert_file)
|
|
74
|
+
keycert_files.push(File.expand_path(keycert_file)).uniq!
|
|
75
|
+
self
|
|
76
|
+
end
|
|
77
|
+
|
|
69
78
|
# Add the given key_file to the list of keys that will be used.
|
|
70
79
|
def add_key_data(key_data_)
|
|
71
80
|
key_data.push(key_data_).uniq!
|
|
@@ -108,7 +117,7 @@ module Net
|
|
|
108
117
|
user_identities.delete(corresponding_user_identity) if corresponding_user_identity
|
|
109
118
|
|
|
110
119
|
if !options[:keys_only] || corresponding_user_identity
|
|
111
|
-
known_identities[key] = { from: :agent }
|
|
120
|
+
known_identities[key] = { from: :agent, identity: key }
|
|
112
121
|
yield key
|
|
113
122
|
end
|
|
114
123
|
end
|
|
@@ -122,6 +131,21 @@ module Net
|
|
|
122
131
|
yield key
|
|
123
132
|
end
|
|
124
133
|
|
|
134
|
+
known_identity_blobs = known_identities.keys.map(&:to_blob)
|
|
135
|
+
keycert_files.each do |keycert_file|
|
|
136
|
+
keycert = KeyFactory.load_public_key(keycert_file)
|
|
137
|
+
next if known_identity_blobs.include?(keycert.to_blob)
|
|
138
|
+
|
|
139
|
+
(_, corresponding_identity) = known_identities.detect { |public_key, _|
|
|
140
|
+
public_key.to_pem == keycert.to_pem
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if corresponding_identity
|
|
144
|
+
known_identities[keycert] = corresponding_identity
|
|
145
|
+
yield keycert
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
125
149
|
self
|
|
126
150
|
end
|
|
127
151
|
|
|
@@ -134,25 +158,39 @@ module Net
|
|
|
134
158
|
# Regardless of the identity's origin or who does the signing, this
|
|
135
159
|
# will always return the signature in an SSH2-specified "signature
|
|
136
160
|
# blob" format.
|
|
137
|
-
def sign(identity, data)
|
|
161
|
+
def sign(identity, data, sig_alg = nil)
|
|
138
162
|
info = known_identities[identity] or raise KeyManagerError, "the given identity is unknown to the key manager"
|
|
139
163
|
|
|
140
164
|
if info[:key].nil? && info[:from] == :file
|
|
141
165
|
begin
|
|
142
|
-
info[:key] = KeyFactory.load_private_key(info[:file], options[:passphrase], !options[:non_interactive])
|
|
166
|
+
info[:key] = KeyFactory.load_private_key(info[:file], options[:passphrase], !options[:non_interactive], options[:password_prompt])
|
|
143
167
|
rescue OpenSSL::OpenSSLError, Exception => e
|
|
144
168
|
raise KeyManagerError, "the given identity is known, but the private key could not be loaded: #{e.class} (#{e.message})"
|
|
145
169
|
end
|
|
146
170
|
end
|
|
147
171
|
|
|
148
172
|
if info[:key]
|
|
149
|
-
|
|
150
|
-
|
|
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
|
|
151
181
|
end
|
|
152
182
|
|
|
153
183
|
if info[:from] == :agent
|
|
154
184
|
raise KeyManagerError, "the agent is no longer available" unless agent
|
|
155
|
-
|
|
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
|
|
156
194
|
end
|
|
157
195
|
|
|
158
196
|
raise KeyManagerError, "[BUG] can't determine identity origin (#{info.inspect})"
|
|
@@ -176,6 +214,7 @@ module Net
|
|
|
176
214
|
# or if the agent is otherwise not available.
|
|
177
215
|
def agent
|
|
178
216
|
return unless use_agent?
|
|
217
|
+
|
|
179
218
|
@agent ||= Agent.connect(logger, options[:agent_socket_factory], options[:identity_agent])
|
|
180
219
|
rescue AgentNotAvailable
|
|
181
220
|
@use_agent = false
|
|
@@ -223,33 +262,35 @@ module Net
|
|
|
223
262
|
# Load prepared identities. Private key decryption errors ignored if ignore_decryption_errors
|
|
224
263
|
def load_identities(identities, ask_passphrase, ignore_decryption_errors)
|
|
225
264
|
identities.map do |identity|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
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
|
|
250
288
|
process_identity_loading_error(identity, e)
|
|
251
289
|
nil
|
|
252
290
|
end
|
|
291
|
+
rescue Exception => e
|
|
292
|
+
process_identity_loading_error(identity, e)
|
|
293
|
+
nil
|
|
253
294
|
end.compact
|
|
254
295
|
end
|
|
255
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
|
|
@@ -5,14 +5,13 @@ module Net
|
|
|
5
5
|
module SSH
|
|
6
6
|
module Authentication
|
|
7
7
|
module Methods
|
|
8
|
-
|
|
9
8
|
# Implements the "keyboard-interactive" SSH authentication method.
|
|
10
9
|
class KeyboardInteractive < Abstract
|
|
11
10
|
USERAUTH_INFO_REQUEST = 60
|
|
12
11
|
USERAUTH_INFO_RESPONSE = 61
|
|
13
12
|
|
|
14
13
|
# Attempt to authenticate the given user for the given service.
|
|
15
|
-
def authenticate(next_service, username, password=nil)
|
|
14
|
+
def authenticate(next_service, username, password = nil)
|
|
16
15
|
debug { "trying keyboard-interactive" }
|
|
17
16
|
send_message(userauth_request(username, next_service, "keyboard-interactive", "", ""))
|
|
18
17
|
|
|
@@ -32,6 +31,7 @@ module Net
|
|
|
32
31
|
message[:authentications].split(/,/).include? 'keyboard-interactive'
|
|
33
32
|
|
|
34
33
|
return false unless interactive?
|
|
34
|
+
|
|
35
35
|
password = nil
|
|
36
36
|
debug { "retrying keyboard-interactive" }
|
|
37
37
|
send_message(userauth_request(username, next_service, "keyboard-interactive", "", ""))
|
|
@@ -40,7 +40,9 @@ module Net
|
|
|
40
40
|
instruction = message.read_string
|
|
41
41
|
debug { "keyboard-interactive info request" }
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
if password.nil? && interactive? && prompter.nil?
|
|
44
|
+
prompter = prompt.start(type: 'keyboard-interactive', name: name, instruction: instruction)
|
|
45
|
+
end
|
|
44
46
|
|
|
45
47
|
_ = message.read_string # lang_tag
|
|
46
48
|
responses = []
|
|
@@ -5,32 +5,29 @@ module Net
|
|
|
5
5
|
module SSH
|
|
6
6
|
module Authentication
|
|
7
7
|
module Methods
|
|
8
|
-
|
|
9
8
|
# Implements the "none" SSH authentication method.
|
|
10
9
|
class None < Abstract
|
|
11
10
|
# Attempt to authenticate as "none"
|
|
12
|
-
def authenticate(next_service, user="", password="")
|
|
13
|
-
send_message(userauth_request(user, next_service, "none"))
|
|
11
|
+
def authenticate(next_service, user = "", password = "")
|
|
12
|
+
send_message(userauth_request(user, next_service, "none"))
|
|
14
13
|
message = session.next_message
|
|
15
|
-
|
|
14
|
+
|
|
16
15
|
case message.type
|
|
17
16
|
when USERAUTH_SUCCESS
|
|
18
17
|
debug { "none succeeded" }
|
|
19
18
|
return true
|
|
20
19
|
when USERAUTH_FAILURE
|
|
21
20
|
debug { "none failed" }
|
|
22
|
-
|
|
21
|
+
|
|
23
22
|
raise Net::SSH::Authentication::DisallowedMethod unless
|
|
24
23
|
message[:authentications].split(/,/).include? 'none'
|
|
25
|
-
|
|
24
|
+
|
|
26
25
|
return false
|
|
27
26
|
else
|
|
28
27
|
raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
|
|
29
|
-
end
|
|
30
|
-
|
|
28
|
+
end
|
|
31
29
|
end
|
|
32
30
|
end
|
|
33
|
-
|
|
34
31
|
end
|
|
35
32
|
end
|
|
36
33
|
end
|
|
@@ -6,12 +6,11 @@ module Net
|
|
|
6
6
|
module SSH
|
|
7
7
|
module Authentication
|
|
8
8
|
module Methods
|
|
9
|
-
|
|
10
9
|
# Implements the "password" SSH authentication method.
|
|
11
10
|
class Password < Abstract
|
|
12
11
|
# Attempt to authenticate the given user for the given service. If
|
|
13
12
|
# the password parameter is nil, this will ask for password
|
|
14
|
-
def authenticate(next_service, username, password=nil)
|
|
13
|
+
def authenticate(next_service, username, password = nil)
|
|
15
14
|
clear_prompter!
|
|
16
15
|
retries = 0
|
|
17
16
|
max_retries = get_max_retries
|
|
@@ -29,6 +28,7 @@ module Net
|
|
|
29
28
|
|
|
30
29
|
raise Net::SSH::Authentication::DisallowedMethod unless
|
|
31
30
|
message[:authentications].split(/,/).include? 'password'
|
|
31
|
+
|
|
32
32
|
password = nil
|
|
33
33
|
end
|
|
34
34
|
end until (message.type != USERAUTH_FAILURE || retries >= max_retries)
|
|
@@ -74,7 +74,6 @@ module Net
|
|
|
74
74
|
options[:non_interactive] ? 0 : result
|
|
75
75
|
end
|
|
76
76
|
end
|
|
77
|
-
|
|
78
77
|
end
|
|
79
78
|
end
|
|
80
79
|
end
|