net-ssh 3.2.0 → 7.2.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/.dockerignore +6 -0
  4. data/.github/FUNDING.yml +1 -0
  5. data/.github/config/rubocop_linter_action.yml +4 -0
  6. data/.github/workflows/ci-with-docker.yml +44 -0
  7. data/.github/workflows/ci.yml +93 -0
  8. data/.github/workflows/rubocop.yml +16 -0
  9. data/.gitignore +13 -0
  10. data/.rubocop.yml +22 -0
  11. data/.rubocop_todo.yml +1081 -0
  12. data/CHANGES.txt +237 -7
  13. data/DEVELOPMENT.md +23 -0
  14. data/Dockerfile +27 -0
  15. data/Dockerfile.openssl3 +17 -0
  16. data/Gemfile +13 -0
  17. data/Gemfile.noed25519 +12 -0
  18. data/Gemfile.norbnacl +12 -0
  19. data/ISSUE_TEMPLATE.md +30 -0
  20. data/Manifest +4 -5
  21. data/README.md +298 -0
  22. data/Rakefile +125 -74
  23. data/SECURITY.md +4 -0
  24. data/appveyor.yml +58 -0
  25. data/docker-compose.yml +23 -0
  26. data/lib/net/ssh/authentication/agent.rb +279 -18
  27. data/lib/net/ssh/authentication/certificate.rb +183 -0
  28. data/lib/net/ssh/authentication/constants.rb +17 -15
  29. data/lib/net/ssh/authentication/ed25519.rb +186 -0
  30. data/lib/net/ssh/authentication/ed25519_loader.rb +31 -0
  31. data/lib/net/ssh/authentication/key_manager.rb +86 -39
  32. data/lib/net/ssh/authentication/methods/abstract.rb +67 -48
  33. data/lib/net/ssh/authentication/methods/hostbased.rb +34 -37
  34. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +13 -13
  35. data/lib/net/ssh/authentication/methods/none.rb +16 -19
  36. data/lib/net/ssh/authentication/methods/password.rb +27 -17
  37. data/lib/net/ssh/authentication/methods/publickey.rb +96 -55
  38. data/lib/net/ssh/authentication/pageant.rb +471 -367
  39. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +43 -0
  40. data/lib/net/ssh/authentication/session.rb +131 -121
  41. data/lib/net/ssh/buffer.rb +399 -300
  42. data/lib/net/ssh/buffered_io.rb +154 -150
  43. data/lib/net/ssh/config.rb +308 -185
  44. data/lib/net/ssh/connection/channel.rb +635 -613
  45. data/lib/net/ssh/connection/constants.rb +29 -29
  46. data/lib/net/ssh/connection/event_loop.rb +123 -0
  47. data/lib/net/ssh/connection/keepalive.rb +55 -51
  48. data/lib/net/ssh/connection/session.rb +620 -551
  49. data/lib/net/ssh/connection/term.rb +125 -123
  50. data/lib/net/ssh/errors.rb +101 -99
  51. data/lib/net/ssh/key_factory.rb +197 -105
  52. data/lib/net/ssh/known_hosts.rb +214 -127
  53. data/lib/net/ssh/loggable.rb +50 -49
  54. data/lib/net/ssh/packet.rb +83 -79
  55. data/lib/net/ssh/prompt.rb +50 -81
  56. data/lib/net/ssh/proxy/command.rb +105 -90
  57. data/lib/net/ssh/proxy/errors.rb +12 -10
  58. data/lib/net/ssh/proxy/http.rb +82 -79
  59. data/lib/net/ssh/proxy/https.rb +50 -0
  60. data/lib/net/ssh/proxy/jump.rb +54 -0
  61. data/lib/net/ssh/proxy/socks4.rb +2 -6
  62. data/lib/net/ssh/proxy/socks5.rb +14 -17
  63. data/lib/net/ssh/service/forward.rb +370 -317
  64. data/lib/net/ssh/test/channel.rb +145 -136
  65. data/lib/net/ssh/test/extensions.rb +131 -110
  66. data/lib/net/ssh/test/kex.rb +34 -32
  67. data/lib/net/ssh/test/local_packet.rb +46 -44
  68. data/lib/net/ssh/test/packet.rb +89 -70
  69. data/lib/net/ssh/test/remote_packet.rb +32 -30
  70. data/lib/net/ssh/test/script.rb +156 -142
  71. data/lib/net/ssh/test/socket.rb +49 -48
  72. data/lib/net/ssh/test.rb +82 -77
  73. data/lib/net/ssh/transport/algorithms.rb +462 -359
  74. data/lib/net/ssh/transport/chacha20_poly1305_cipher.rb +117 -0
  75. data/lib/net/ssh/transport/chacha20_poly1305_cipher_loader.rb +17 -0
  76. data/lib/net/ssh/transport/cipher_factory.rb +122 -99
  77. data/lib/net/ssh/transport/constants.rb +32 -24
  78. data/lib/net/ssh/transport/ctr.rb +42 -22
  79. data/lib/net/ssh/transport/hmac/abstract.rb +81 -63
  80. data/lib/net/ssh/transport/hmac/md5.rb +0 -2
  81. data/lib/net/ssh/transport/hmac/md5_96.rb +0 -2
  82. data/lib/net/ssh/transport/hmac/none.rb +0 -2
  83. data/lib/net/ssh/transport/hmac/ripemd160.rb +0 -2
  84. data/lib/net/ssh/transport/hmac/sha1.rb +0 -2
  85. data/lib/net/ssh/transport/hmac/sha1_96.rb +0 -2
  86. data/lib/net/ssh/transport/hmac/sha2_256.rb +7 -11
  87. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +4 -8
  88. data/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
  89. data/lib/net/ssh/transport/hmac/sha2_512.rb +6 -9
  90. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +4 -8
  91. data/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
  92. data/lib/net/ssh/transport/hmac.rb +14 -12
  93. data/lib/net/ssh/transport/identity_cipher.rb +54 -44
  94. data/lib/net/ssh/transport/kex/abstract.rb +130 -0
  95. data/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
  96. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +39 -0
  97. data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
  98. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +33 -40
  99. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
  100. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +119 -213
  101. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +53 -61
  102. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +5 -9
  103. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +36 -90
  104. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +18 -10
  105. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +18 -10
  106. data/lib/net/ssh/transport/kex.rb +15 -12
  107. data/lib/net/ssh/transport/key_expander.rb +24 -20
  108. data/lib/net/ssh/transport/openssl.rb +161 -124
  109. data/lib/net/ssh/transport/openssl_cipher_extensions.rb +8 -0
  110. data/lib/net/ssh/transport/packet_stream.rb +246 -185
  111. data/lib/net/ssh/transport/server_version.rb +55 -56
  112. data/lib/net/ssh/transport/session.rb +306 -255
  113. data/lib/net/ssh/transport/state.rb +178 -176
  114. data/lib/net/ssh/verifiers/accept_new.rb +33 -0
  115. data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +33 -0
  116. data/lib/net/ssh/verifiers/always.rb +58 -0
  117. data/lib/net/ssh/verifiers/never.rb +19 -0
  118. data/lib/net/ssh/version.rb +55 -53
  119. data/lib/net/ssh.rb +111 -47
  120. data/net-ssh-public_cert.pem +18 -18
  121. data/net-ssh.gemspec +38 -205
  122. data/support/ssh_tunnel_bug.rb +5 -5
  123. data.tar.gz.sig +0 -0
  124. metadata +173 -118
  125. metadata.gz.sig +0 -0
  126. data/.travis.yml +0 -18
  127. data/README.rdoc +0 -182
  128. data/lib/net/ssh/authentication/agent/java_pageant.rb +0 -85
  129. data/lib/net/ssh/authentication/agent/socket.rb +0 -178
  130. data/lib/net/ssh/ruby_compat.rb +0 -46
  131. data/lib/net/ssh/verifiers/lenient.rb +0 -30
  132. data/lib/net/ssh/verifiers/null.rb +0 -12
  133. data/lib/net/ssh/verifiers/secure.rb +0 -52
  134. data/lib/net/ssh/verifiers/strict.rb +0 -24
  135. data/setup.rb +0 -1585
  136. data/support/arcfour_check.rb +0 -20
  137. data/test/README.txt +0 -18
  138. data/test/authentication/methods/common.rb +0 -28
  139. data/test/authentication/methods/test_abstract.rb +0 -51
  140. data/test/authentication/methods/test_hostbased.rb +0 -114
  141. data/test/authentication/methods/test_keyboard_interactive.rb +0 -121
  142. data/test/authentication/methods/test_none.rb +0 -41
  143. data/test/authentication/methods/test_password.rb +0 -95
  144. data/test/authentication/methods/test_publickey.rb +0 -148
  145. data/test/authentication/test_agent.rb +0 -232
  146. data/test/authentication/test_key_manager.rb +0 -240
  147. data/test/authentication/test_session.rb +0 -107
  148. data/test/common.rb +0 -125
  149. data/test/configs/auth_off +0 -5
  150. data/test/configs/auth_on +0 -4
  151. data/test/configs/empty +0 -0
  152. data/test/configs/eqsign +0 -3
  153. data/test/configs/exact_match +0 -8
  154. data/test/configs/host_plus +0 -10
  155. data/test/configs/multihost +0 -4
  156. data/test/configs/negative_match +0 -6
  157. data/test/configs/nohost +0 -19
  158. data/test/configs/numeric_host +0 -4
  159. data/test/configs/proxy_remote_user +0 -2
  160. data/test/configs/send_env +0 -2
  161. data/test/configs/substitutes +0 -8
  162. data/test/configs/wild_cards +0 -14
  163. data/test/connection/test_channel.rb +0 -487
  164. data/test/connection/test_session.rb +0 -564
  165. data/test/integration/README.txt +0 -17
  166. data/test/integration/Vagrantfile +0 -12
  167. data/test/integration/common.rb +0 -63
  168. data/test/integration/playbook.yml +0 -56
  169. data/test/integration/test_forward.rb +0 -637
  170. data/test/integration/test_id_rsa_keys.rb +0 -96
  171. data/test/integration/test_proxy.rb +0 -93
  172. data/test/known_hosts/github +0 -1
  173. data/test/known_hosts/github_hash +0 -1
  174. data/test/manual/test_pageant.rb +0 -37
  175. data/test/start/test_connection.rb +0 -53
  176. data/test/start/test_options.rb +0 -57
  177. data/test/start/test_transport.rb +0 -28
  178. data/test/start/test_user_nil.rb +0 -27
  179. data/test/test_all.rb +0 -12
  180. data/test/test_buffer.rb +0 -433
  181. data/test/test_buffered_io.rb +0 -63
  182. data/test/test_config.rb +0 -268
  183. data/test/test_key_factory.rb +0 -191
  184. data/test/test_known_hosts.rb +0 -66
  185. data/test/transport/hmac/test_md5.rb +0 -41
  186. data/test/transport/hmac/test_md5_96.rb +0 -27
  187. data/test/transport/hmac/test_none.rb +0 -34
  188. data/test/transport/hmac/test_ripemd160.rb +0 -36
  189. data/test/transport/hmac/test_sha1.rb +0 -36
  190. data/test/transport/hmac/test_sha1_96.rb +0 -27
  191. data/test/transport/hmac/test_sha2_256.rb +0 -37
  192. data/test/transport/hmac/test_sha2_256_96.rb +0 -27
  193. data/test/transport/hmac/test_sha2_512.rb +0 -37
  194. data/test/transport/hmac/test_sha2_512_96.rb +0 -27
  195. data/test/transport/kex/test_diffie_hellman_group14_sha1.rb +0 -13
  196. data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +0 -150
  197. data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +0 -96
  198. data/test/transport/kex/test_diffie_hellman_group_exchange_sha256.rb +0 -19
  199. data/test/transport/kex/test_ecdh_sha2_nistp256.rb +0 -161
  200. data/test/transport/kex/test_ecdh_sha2_nistp384.rb +0 -38
  201. data/test/transport/kex/test_ecdh_sha2_nistp521.rb +0 -38
  202. data/test/transport/test_algorithms.rb +0 -328
  203. data/test/transport/test_cipher_factory.rb +0 -443
  204. data/test/transport/test_hmac.rb +0 -34
  205. data/test/transport/test_identity_cipher.rb +0 -40
  206. data/test/transport/test_packet_stream.rb +0 -1762
  207. data/test/transport/test_server_version.rb +0 -74
  208. data/test/transport/test_session.rb +0 -331
  209. data/test/transport/test_state.rb +0 -181
  210. data/test/verifiers/test_secure.rb +0 -40
@@ -0,0 +1,186 @@
1
+ gem 'ed25519', '~> 1.2'
2
+ gem 'bcrypt_pbkdf', '~> 1.0' unless RUBY_PLATFORM == "java"
3
+
4
+ require 'ed25519'
5
+
6
+ require 'base64'
7
+
8
+ require 'net/ssh/transport/cipher_factory'
9
+ require 'net/ssh/authentication/pub_key_fingerprint'
10
+ require 'bcrypt_pbkdf' unless RUBY_PLATFORM == "java"
11
+
12
+ module Net
13
+ module SSH
14
+ module Authentication
15
+ module ED25519
16
+ class SigningKeyFromFile < SimpleDelegator
17
+ def initialize(pk, sk)
18
+ key = ::Ed25519::SigningKey.from_keypair(sk)
19
+ raise ArgumentError, "pk does not match sk" unless pk == key.verify_key.to_bytes
20
+
21
+ super(key)
22
+ end
23
+ end
24
+
25
+ class OpenSSHPrivateKeyLoader
26
+ CipherFactory = Net::SSH::Transport::CipherFactory
27
+
28
+ MBEGIN = "-----BEGIN OPENSSH PRIVATE KEY-----\n"
29
+ MEND = "-----END OPENSSH PRIVATE KEY-----"
30
+ MAGIC = "openssh-key-v1"
31
+
32
+ class DecryptError < ArgumentError
33
+ def initialize(message, encrypted_key: false)
34
+ super(message)
35
+ @encrypted_key = encrypted_key
36
+ end
37
+
38
+ def encrypted_key?
39
+ return @encrypted_key
40
+ end
41
+ end
42
+
43
+ def self.read(datafull, password)
44
+ datafull = datafull.strip
45
+ raise ArgumentError.new("Expected #{MBEGIN} at start of private key") unless datafull.start_with?(MBEGIN)
46
+ raise ArgumentError.new("Expected #{MEND} at end of private key") unless datafull.end_with?(MEND)
47
+
48
+ datab64 = datafull[MBEGIN.size...-MEND.size]
49
+ data = Base64.decode64(datab64)
50
+ raise ArgumentError.new("Expected #{MAGIC} at start of decoded private key") unless data.start_with?(MAGIC)
51
+
52
+ buffer = Net::SSH::Buffer.new(data[MAGIC.size + 1..-1])
53
+
54
+ ciphername = buffer.read_string
55
+ raise ArgumentError.new("#{ciphername} in private key is not supported") unless
56
+ CipherFactory.supported?(ciphername)
57
+
58
+ kdfname = buffer.read_string
59
+ raise ArgumentError.new("Expected #{kdfname} to be or none or bcrypt") unless %w[none bcrypt].include?(kdfname)
60
+
61
+ kdfopts = Net::SSH::Buffer.new(buffer.read_string)
62
+ num_keys = buffer.read_long
63
+ raise ArgumentError.new("Only 1 key is supported in ssh keys #{num_keys} was in private key") unless num_keys == 1
64
+
65
+ _pubkey = buffer.read_string
66
+
67
+ len = buffer.read_long
68
+
69
+ keylen, blocksize, ivlen = CipherFactory.get_lengths(ciphername, iv_len: true)
70
+ raise ArgumentError.new("Private key len:#{len} is not a multiple of #{blocksize}") if
71
+ ((len < blocksize) || ((blocksize > 0) && (len % blocksize) != 0))
72
+
73
+ if kdfname == 'bcrypt'
74
+ salt = kdfopts.read_string
75
+ rounds = kdfopts.read_long
76
+
77
+ raise "BCryptPbkdf is not implemented for jruby" if RUBY_PLATFORM == "java"
78
+
79
+ key = BCryptPbkdf::key(password, salt, keylen + ivlen, rounds)
80
+ raise DecryptError.new("BCyryptPbkdf failed", encrypted_key: true) unless key
81
+ else
82
+ key = '\x00' * (keylen + ivlen)
83
+ end
84
+
85
+ cipher = CipherFactory.get(ciphername, key: key[0...keylen], iv: key[keylen...keylen + ivlen], decrypt: true)
86
+
87
+ decoded = cipher.update(buffer.remainder_as_buffer.to_s)
88
+ decoded << cipher.final
89
+
90
+ decoded = Net::SSH::Buffer.new(decoded)
91
+ check1 = decoded.read_long
92
+ check2 = decoded.read_long
93
+
94
+ raise DecryptError.new("Decrypt failed on private key", encrypted_key: kdfname == 'bcrypt') if (check1 != check2)
95
+
96
+ type_name = decoded.read_string
97
+ case type_name
98
+ when "ssh-ed25519"
99
+ PrivKey.new(decoded)
100
+ else
101
+ decoded.read_private_keyblob(type_name)
102
+ end
103
+ end
104
+ end
105
+
106
+ class PubKey
107
+ include Net::SSH::Authentication::PubKeyFingerprint
108
+
109
+ attr_reader :verify_key
110
+
111
+ def initialize(data)
112
+ @verify_key = ::Ed25519::VerifyKey.new(data)
113
+ end
114
+
115
+ def self.read_keyblob(buffer)
116
+ PubKey.new(buffer.read_string)
117
+ end
118
+
119
+ def to_blob
120
+ Net::SSH::Buffer.from(:mstring, "ssh-ed25519".dup, :string, @verify_key.to_bytes).to_s
121
+ end
122
+
123
+ def ssh_type
124
+ "ssh-ed25519"
125
+ end
126
+
127
+ def ssh_signature_type
128
+ ssh_type
129
+ end
130
+
131
+ def ssh_do_verify(sig, data, options = {})
132
+ @verify_key.verify(sig, data)
133
+ end
134
+
135
+ def to_pem
136
+ # TODO this is not pem
137
+ ssh_type + Base64.encode64(@verify_key.to_bytes)
138
+ end
139
+ end
140
+
141
+ class PrivKey
142
+ CipherFactory = Net::SSH::Transport::CipherFactory
143
+
144
+ MBEGIN = "-----BEGIN OPENSSH PRIVATE KEY-----\n"
145
+ MEND = "-----END OPENSSH PRIVATE KEY-----\n"
146
+ MAGIC = "openssh-key-v1"
147
+
148
+ attr_reader :sign_key
149
+
150
+ def initialize(buffer)
151
+ pk = buffer.read_string
152
+ sk = buffer.read_string
153
+ _comment = buffer.read_string
154
+
155
+ @pk = pk
156
+ @sign_key = SigningKeyFromFile.new(pk, sk)
157
+ end
158
+
159
+ def to_blob
160
+ public_key.to_blob
161
+ end
162
+
163
+ def ssh_type
164
+ "ssh-ed25519"
165
+ end
166
+
167
+ def ssh_signature_type
168
+ ssh_type
169
+ end
170
+
171
+ def public_key
172
+ PubKey.new(@pk)
173
+ end
174
+
175
+ def ssh_do_sign(data, sig_alg = nil)
176
+ @sign_key.sign(data)
177
+ end
178
+
179
+ def self.read(data, password)
180
+ OpenSSHPrivateKeyLoader.read(data, password)
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,31 @@
1
+ module Net
2
+ module SSH
3
+ module Authentication
4
+ # Loads ED25519 support which requires optinal dependecies like
5
+ # ed25519, bcrypt_pbkdf
6
+ module ED25519Loader
7
+ begin
8
+ require 'net/ssh/authentication/ed25519'
9
+ LOADED = true
10
+ ERROR = nil
11
+ rescue LoadError => e
12
+ ERROR = e
13
+ LOADED = false
14
+ end
15
+
16
+ def self.raiseUnlessLoaded(message)
17
+ description = ERROR.is_a?(LoadError) ? dependenciesRequiredForED25519 : ''
18
+ description << "#{ERROR.class} : \"#{ERROR.message}\"\n" if ERROR
19
+ raise NotImplementedError, "#{message}\n#{description}" unless LOADED
20
+ end
21
+
22
+ def self.dependenciesRequiredForED25519
23
+ result = "net-ssh requires the following gems for ed25519 support:\n"
24
+ result << " * ed25519 (>= 1.2, < 2.0)\n"
25
+ result << " * bcrypt_pbkdf (>= 1.0, < 2.0)\n" unless RUBY_PLATFORM == "java"
26
+ result << "See https://github.com/net-ssh/net-ssh/issues/565 for more information\n"
27
+ end
28
+ end
29
+ end
30
+ end
31
+ 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,11 +41,12 @@ 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 = []
46
- @use_agent = !(options[:use_agent] == false)
48
+ @keycert_files = []
49
+ @use_agent = options[:use_agent] != false
47
50
  @known_identities = {}
48
51
  @agent = nil
49
52
  @options = options
@@ -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])
143
- rescue Exception, OpenSSL::OpenSSLError => e
166
+ info[:key] = KeyFactory.load_private_key(info[:file], options[:passphrase], !options[:non_interactive], options[:password_prompt])
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
- return Net::SSH::Buffer.from(:string, identity.ssh_type,
150
- :string, 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
151
181
  end
152
182
 
153
183
  if info[:from] == :agent
154
184
  raise KeyManagerError, "the agent is no longer available" unless agent
155
- return agent.sign(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
156
194
  end
157
195
 
158
196
  raise KeyManagerError, "[BUG] can't determine identity origin (#{info.inspect})"
@@ -176,12 +214,17 @@ module Net
176
214
  # or if the agent is otherwise not available.
177
215
  def agent
178
216
  return unless use_agent?
179
- @agent ||= Agent.connect(logger, options[:agent_socket_factory])
217
+
218
+ @agent ||= Agent.connect(logger, options[:agent_socket_factory], options[:identity_agent])
180
219
  rescue AgentNotAvailable
181
220
  @use_agent = false
182
221
  nil
183
222
  end
184
223
 
224
+ def no_keys?
225
+ key_files.empty? && key_data.empty?
226
+ end
227
+
185
228
  private
186
229
 
187
230
  # Prepares identities from user key_files for loading, preserving their order and sources.
@@ -189,8 +232,12 @@ module Net
189
232
  key_files.map do |file|
190
233
  if readable_file?(file)
191
234
  identity = {}
235
+ cert_file = file + "-cert.pub"
192
236
  public_key_file = file + ".pub"
193
- if readable_file?(public_key_file)
237
+ if readable_file?(cert_file)
238
+ identity[:load_from] = :pubkey_file
239
+ identity[:pubkey_file] = cert_file
240
+ elsif readable_file?(public_key_file)
194
241
  identity[:load_from] = :pubkey_file
195
242
  identity[:pubkey_file] = public_key_file
196
243
  else
@@ -208,41 +255,42 @@ module Net
208
255
  # Prepared identities from user key_data, preserving their order and sources.
209
256
  def prepare_identities_from_data
210
257
  key_data.map do |data|
211
- { :load_from => :data, :data => data }
258
+ { load_from: :data, data: data }
212
259
  end
213
260
  end
214
261
 
215
262
  # Load prepared identities. Private key decryption errors ignored if ignore_decryption_errors
216
263
  def load_identities(identities, ask_passphrase, ignore_decryption_errors)
217
264
  identities.map do |identity|
218
- begin
219
- case identity[:load_from]
220
- when :pubkey_file
221
- key = KeyFactory.load_public_key(identity[:pubkey_file])
222
- { :public_key => key, :from => :file, :file => identity[:privkey_file] }
223
- when :privkey_file
224
- private_key = KeyFactory.load_private_key(identity[:privkey_file], options[:passphrase], ask_passphrase)
225
- key = private_key.send(:public_key)
226
- { :public_key => key, :from => :file, :file => identity[:privkey_file], :key => private_key }
227
- when :data
228
- private_key = KeyFactory.load_data_private_key(identity[:data], options[:passphrase], ask_passphrase)
229
- key = private_key.send(:public_key)
230
- { :public_key => key, :from => :key_data, :data => identity[:data], :key => private_key }
231
- else
232
- identity
233
- end
234
-
235
- rescue OpenSSL::PKey::RSAError, OpenSSL::PKey::DSAError, OpenSSL::PKey::ECError, ArgumentError => e
236
- if ignore_decryption_errors
237
- identity
238
- else
239
- process_identity_loading_error(identity, e)
240
- nil
241
- end
242
- 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
243
288
  process_identity_loading_error(identity, e)
244
289
  nil
245
290
  end
291
+ rescue Exception => e
292
+ process_identity_loading_error(identity, e)
293
+ nil
246
294
  end.compact
247
295
  end
248
296
 
@@ -256,7 +304,6 @@ module Net
256
304
  raise e
257
305
  end
258
306
  end
259
-
260
307
  end
261
308
  end
262
309
  end
@@ -3,58 +3,77 @@ require 'net/ssh/errors'
3
3
  require 'net/ssh/loggable'
4
4
  require 'net/ssh/authentication/constants'
5
5
 
6
- module Net; module SSH; module Authentication; module Methods
7
-
8
- # The base class of all user authentication methods. It provides a few
9
- # bits of common functionality.
10
- class Abstract
11
- include Constants, Loggable
12
-
13
- # The authentication session object
14
- attr_reader :session
15
-
16
- # The key manager object. Not all authentication methods will require
17
- # this.
18
- attr_reader :key_manager
19
-
20
- # Instantiates a new authentication method.
21
- def initialize(session, options={})
22
- @session = session
23
- @key_manager = options[:key_manager]
24
- @options = options
25
- self.logger = session.logger
26
- end
6
+ module Net
7
+ module SSH
8
+ module Authentication
9
+ module Methods
10
+ # The base class of all user authentication methods. It provides a few
11
+ # bits of common functionality.
12
+ class Abstract
13
+ include Loggable
14
+ include Constants
27
15
 
28
- # Returns the session-id, as generated during the first key exchange of
29
- # an SSH connection.
30
- def session_id
31
- session.transport.algorithms.session_id
32
- end
16
+ # The authentication session object
17
+ attr_reader :session
33
18
 
34
- # Sends a message via the underlying transport layer abstraction. This
35
- # will block until the message is completely sent.
36
- def send_message(msg)
37
- session.transport.send_message(msg)
38
- end
19
+ # The key manager object. Not all authentication methods will require
20
+ # this.
21
+ attr_reader :key_manager
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
+
28
+ # Instantiates a new authentication method.
29
+ def initialize(session, options = {})
30
+ @session = session
31
+ @key_manager = options[:key_manager]
32
+ @options = options
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]
39
+ self.logger = session.logger
40
+ end
41
+
42
+ # Returns the session-id, as generated during the first key exchange of
43
+ # an SSH connection.
44
+ def session_id
45
+ session.transport.algorithms.session_id
46
+ end
47
+
48
+ # Sends a message via the underlying transport layer abstraction. This
49
+ # will block until the message is completely sent.
50
+ def send_message(msg)
51
+ session.transport.send_message(msg)
52
+ end
39
53
 
40
- # Creates a new USERAUTH_REQUEST packet. The extra arguments on the end
41
- # must be either boolean values or strings, and are tacked onto the end
42
- # of the packet. The new packet is returned, ready for sending.
43
- def userauth_request(username, next_service, auth_method, *others)
44
- buffer = Net::SSH::Buffer.from(:byte, USERAUTH_REQUEST,
45
- :string, username, :string, next_service, :string, auth_method)
46
-
47
- others.each do |value|
48
- case value
49
- when true, false then buffer.write_bool(value)
50
- when String then buffer.write_string(value)
51
- else raise ArgumentError, "don't know how to write #{value.inspect}"
54
+ # Creates a new USERAUTH_REQUEST packet. The extra arguments on the end
55
+ # must be either boolean values or strings, and are tacked onto the end
56
+ # of the packet. The new packet is returned, ready for sending.
57
+ def userauth_request(username, next_service, auth_method, *others)
58
+ buffer = Net::SSH::Buffer.from(:byte, USERAUTH_REQUEST,
59
+ :string, username, :string, next_service, :string, auth_method)
60
+
61
+ others.each do |value|
62
+ case value
63
+ when true, false then buffer.write_bool(value)
64
+ when String then buffer.write_string(value)
65
+ else raise ArgumentError, "don't know how to write #{value.inspect}"
66
+ end
67
+ end
68
+
69
+ buffer
70
+ end
71
+
72
+ private
73
+
74
+ attr_reader :prompt
52
75
  end
53
76
  end
54
-
55
- buffer
56
77
  end
57
-
58
78
  end
59
-
60
- end; end; end; end
79
+ end
@@ -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
@@ -24,51 +23,49 @@ module Net
24
23
 
25
24
  private
26
25
 
27
- # Returns the hostname as reported by the underlying socket.
28
- def hostname
29
- session.transport.socket.client_name
30
- end
31
-
32
- # Attempts to perform host-based authentication of the user, using
33
- # the given host identity (key).
34
- def authenticate_with(identity, next_service, username, key_manager)
35
- debug { "trying hostbased (#{identity.fingerprint})" }
36
- client_username = ENV['USER'] || username
26
+ # Returns the hostname as reported by the underlying socket.
27
+ def hostname
28
+ session.transport.socket.client_name
29
+ end
37
30
 
38
- req = build_request(identity, next_service, username, "#{hostname}.", client_username)
39
- sig_data = Buffer.from(:string, session_id, :raw, req)
31
+ # Attempts to perform host-based authentication of the user, using
32
+ # the given host identity (key).
33
+ def authenticate_with(identity, next_service, username, key_manager)
34
+ debug { "trying hostbased (#{identity.fingerprint})" }
35
+ client_username = ENV['USER'] || username
40
36
 
41
- sig = key_manager.sign(identity, sig_data.to_s)
37
+ req = build_request(identity, next_service, username, "#{hostname}.", client_username)
38
+ sig_data = Buffer.from(:string, session_id, :raw, req)
42
39
 
43
- message = Buffer.from(:raw, req, :string, sig)
40
+ sig = key_manager.sign(identity, sig_data.to_s)
44
41
 
45
- send_message(message)
46
- message = session.next_message
42
+ message = Buffer.from(:raw, req, :string, sig)
47
43
 
48
- case message.type
49
- when USERAUTH_SUCCESS
50
- info { "hostbased succeeded (#{identity.fingerprint})" }
51
- return true
52
- when USERAUTH_FAILURE
53
- info { "hostbased failed (#{identity.fingerprint})" }
44
+ send_message(message)
45
+ message = session.next_message
54
46
 
55
- raise Net::SSH::Authentication::DisallowedMethod unless
56
- message[:authentications].split(/,/).include? 'hostbased'
47
+ case message.type
48
+ when USERAUTH_SUCCESS
49
+ info { "hostbased succeeded (#{identity.fingerprint})" }
50
+ return true
51
+ when USERAUTH_FAILURE
52
+ info { "hostbased failed (#{identity.fingerprint})" }
57
53
 
58
- return false
59
- else
60
- raise Net::SSH::Exception, "unexpected server response to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
61
- end
62
- end
54
+ raise Net::SSH::Authentication::DisallowedMethod unless
55
+ message[:authentications].split(/,/).include? 'hostbased'
63
56
 
64
- # Build the "core" hostbased request string.
65
- def build_request(identity, next_service, username, hostname, client_username)
66
- userauth_request(username, next_service, "hostbased", identity.ssh_type,
67
- Buffer.from(:key, identity).to_s, hostname, client_username).to_s
57
+ return false
58
+ else
59
+ raise Net::SSH::Exception, "unexpected server response to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
68
60
  end
61
+ end
69
62
 
63
+ # Build the "core" hostbased request string.
64
+ def build_request(identity, next_service, username, hostname, client_username)
65
+ userauth_request(username, next_service, "hostbased", identity.ssh_type,
66
+ Buffer.from(:key, identity).to_s, hostname, client_username).to_s
67
+ end
70
68
  end
71
-
72
69
  end
73
70
  end
74
71
  end