super-fast-gem 0.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.
Files changed (120) hide show
  1. checksums.yaml +7 -0
  2. data/net-ssh-7.3.3/CHANGES.txt +764 -0
  3. data/net-ssh-7.3.3/DEVELOPMENT.md +23 -0
  4. data/net-ssh-7.3.3/Dockerfile +29 -0
  5. data/net-ssh-7.3.3/Dockerfile.openssl3 +17 -0
  6. data/net-ssh-7.3.3/Dockerfile.zlib_ng +29 -0
  7. data/net-ssh-7.3.3/Gemfile +13 -0
  8. data/net-ssh-7.3.3/Gemfile.noed25519 +12 -0
  9. data/net-ssh-7.3.3/Gemfile.norbnacl +12 -0
  10. data/net-ssh-7.3.3/ISSUE_TEMPLATE.md +30 -0
  11. data/net-ssh-7.3.3/LICENSE.txt +19 -0
  12. data/net-ssh-7.3.3/Manifest +132 -0
  13. data/net-ssh-7.3.3/README.md +303 -0
  14. data/net-ssh-7.3.3/Rakefile +196 -0
  15. data/net-ssh-7.3.3/SECURITY.md +4 -0
  16. data/net-ssh-7.3.3/THANKS.txt +110 -0
  17. data/net-ssh-7.3.3/appveyor.yml +58 -0
  18. data/net-ssh-7.3.3/docker-compose.yml +23 -0
  19. data/net-ssh-7.3.3/lib/net/ssh/authentication/agent.rb +284 -0
  20. data/net-ssh-7.3.3/lib/net/ssh/authentication/certificate.rb +183 -0
  21. data/net-ssh-7.3.3/lib/net/ssh/authentication/constants.rb +20 -0
  22. data/net-ssh-7.3.3/lib/net/ssh/authentication/ed25519.rb +184 -0
  23. data/net-ssh-7.3.3/lib/net/ssh/authentication/ed25519_loader.rb +31 -0
  24. data/net-ssh-7.3.3/lib/net/ssh/authentication/key_manager.rb +342 -0
  25. data/net-ssh-7.3.3/lib/net/ssh/authentication/methods/abstract.rb +79 -0
  26. data/net-ssh-7.3.3/lib/net/ssh/authentication/methods/hostbased.rb +72 -0
  27. data/net-ssh-7.3.3/lib/net/ssh/authentication/methods/keyboard_interactive.rb +77 -0
  28. data/net-ssh-7.3.3/lib/net/ssh/authentication/methods/none.rb +34 -0
  29. data/net-ssh-7.3.3/lib/net/ssh/authentication/methods/password.rb +80 -0
  30. data/net-ssh-7.3.3/lib/net/ssh/authentication/methods/publickey.rb +137 -0
  31. data/net-ssh-7.3.3/lib/net/ssh/authentication/pageant.rb +497 -0
  32. data/net-ssh-7.3.3/lib/net/ssh/authentication/pub_key_fingerprint.rb +43 -0
  33. data/net-ssh-7.3.3/lib/net/ssh/authentication/session.rb +172 -0
  34. data/net-ssh-7.3.3/lib/net/ssh/buffer.rb +449 -0
  35. data/net-ssh-7.3.3/lib/net/ssh/buffered_io.rb +202 -0
  36. data/net-ssh-7.3.3/lib/net/ssh/config.rb +407 -0
  37. data/net-ssh-7.3.3/lib/net/ssh/connection/channel.rb +694 -0
  38. data/net-ssh-7.3.3/lib/net/ssh/connection/constants.rb +33 -0
  39. data/net-ssh-7.3.3/lib/net/ssh/connection/event_loop.rb +123 -0
  40. data/net-ssh-7.3.3/lib/net/ssh/connection/keepalive.rb +59 -0
  41. data/net-ssh-7.3.3/lib/net/ssh/connection/session.rb +712 -0
  42. data/net-ssh-7.3.3/lib/net/ssh/connection/term.rb +180 -0
  43. data/net-ssh-7.3.3/lib/net/ssh/errors.rb +106 -0
  44. data/net-ssh-7.3.3/lib/net/ssh/key_factory.rb +218 -0
  45. data/net-ssh-7.3.3/lib/net/ssh/known_hosts.rb +266 -0
  46. data/net-ssh-7.3.3/lib/net/ssh/loggable.rb +62 -0
  47. data/net-ssh-7.3.3/lib/net/ssh/packet.rb +106 -0
  48. data/net-ssh-7.3.3/lib/net/ssh/prompt.rb +62 -0
  49. data/net-ssh-7.3.3/lib/net/ssh/proxy/command.rb +123 -0
  50. data/net-ssh-7.3.3/lib/net/ssh/proxy/errors.rb +16 -0
  51. data/net-ssh-7.3.3/lib/net/ssh/proxy/http.rb +98 -0
  52. data/net-ssh-7.3.3/lib/net/ssh/proxy/https.rb +50 -0
  53. data/net-ssh-7.3.3/lib/net/ssh/proxy/jump.rb +54 -0
  54. data/net-ssh-7.3.3/lib/net/ssh/proxy/socks4.rb +67 -0
  55. data/net-ssh-7.3.3/lib/net/ssh/proxy/socks5.rb +140 -0
  56. data/net-ssh-7.3.3/lib/net/ssh/service/forward.rb +426 -0
  57. data/net-ssh-7.3.3/lib/net/ssh/test/channel.rb +147 -0
  58. data/net-ssh-7.3.3/lib/net/ssh/test/extensions.rb +174 -0
  59. data/net-ssh-7.3.3/lib/net/ssh/test/kex.rb +46 -0
  60. data/net-ssh-7.3.3/lib/net/ssh/test/local_packet.rb +53 -0
  61. data/net-ssh-7.3.3/lib/net/ssh/test/packet.rb +101 -0
  62. data/net-ssh-7.3.3/lib/net/ssh/test/remote_packet.rb +40 -0
  63. data/net-ssh-7.3.3/lib/net/ssh/test/script.rb +180 -0
  64. data/net-ssh-7.3.3/lib/net/ssh/test/socket.rb +65 -0
  65. data/net-ssh-7.3.3/lib/net/ssh/test.rb +94 -0
  66. data/net-ssh-7.3.3/lib/net/ssh/transport/aes128_gcm.rb +40 -0
  67. data/net-ssh-7.3.3/lib/net/ssh/transport/aes256_gcm.rb +40 -0
  68. data/net-ssh-7.3.3/lib/net/ssh/transport/algorithms.rb +531 -0
  69. data/net-ssh-7.3.3/lib/net/ssh/transport/chacha20_poly1305_cipher.rb +117 -0
  70. data/net-ssh-7.3.3/lib/net/ssh/transport/chacha20_poly1305_cipher_loader.rb +17 -0
  71. data/net-ssh-7.3.3/lib/net/ssh/transport/cipher_factory.rb +130 -0
  72. data/net-ssh-7.3.3/lib/net/ssh/transport/constants.rb +40 -0
  73. data/net-ssh-7.3.3/lib/net/ssh/transport/ctr.rb +115 -0
  74. data/net-ssh-7.3.3/lib/net/ssh/transport/gcm_cipher.rb +207 -0
  75. data/net-ssh-7.3.3/lib/net/ssh/transport/hmac/abstract.rb +113 -0
  76. data/net-ssh-7.3.3/lib/net/ssh/transport/hmac/md5.rb +10 -0
  77. data/net-ssh-7.3.3/lib/net/ssh/transport/hmac/md5_96.rb +9 -0
  78. data/net-ssh-7.3.3/lib/net/ssh/transport/hmac/none.rb +13 -0
  79. data/net-ssh-7.3.3/lib/net/ssh/transport/hmac/ripemd160.rb +11 -0
  80. data/net-ssh-7.3.3/lib/net/ssh/transport/hmac/sha1.rb +11 -0
  81. data/net-ssh-7.3.3/lib/net/ssh/transport/hmac/sha1_96.rb +9 -0
  82. data/net-ssh-7.3.3/lib/net/ssh/transport/hmac/sha2_256.rb +11 -0
  83. data/net-ssh-7.3.3/lib/net/ssh/transport/hmac/sha2_256_96.rb +9 -0
  84. data/net-ssh-7.3.3/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
  85. data/net-ssh-7.3.3/lib/net/ssh/transport/hmac/sha2_512.rb +11 -0
  86. data/net-ssh-7.3.3/lib/net/ssh/transport/hmac/sha2_512_96.rb +9 -0
  87. data/net-ssh-7.3.3/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
  88. data/net-ssh-7.3.3/lib/net/ssh/transport/hmac.rb +47 -0
  89. data/net-ssh-7.3.3/lib/net/ssh/transport/identity_cipher.rb +65 -0
  90. data/net-ssh-7.3.3/lib/net/ssh/transport/kex/abstract.rb +130 -0
  91. data/net-ssh-7.3.3/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
  92. data/net-ssh-7.3.3/lib/net/ssh/transport/kex/curve25519_sha256.rb +39 -0
  93. data/net-ssh-7.3.3/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
  94. data/net-ssh-7.3.3/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +37 -0
  95. data/net-ssh-7.3.3/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
  96. data/net-ssh-7.3.3/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +122 -0
  97. data/net-ssh-7.3.3/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +72 -0
  98. data/net-ssh-7.3.3/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +11 -0
  99. data/net-ssh-7.3.3/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +39 -0
  100. data/net-ssh-7.3.3/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +21 -0
  101. data/net-ssh-7.3.3/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +21 -0
  102. data/net-ssh-7.3.3/lib/net/ssh/transport/kex.rb +31 -0
  103. data/net-ssh-7.3.3/lib/net/ssh/transport/key_expander.rb +30 -0
  104. data/net-ssh-7.3.3/lib/net/ssh/transport/openssl.rb +274 -0
  105. data/net-ssh-7.3.3/lib/net/ssh/transport/openssl_cipher_extensions.rb +8 -0
  106. data/net-ssh-7.3.3/lib/net/ssh/transport/packet_stream.rb +301 -0
  107. data/net-ssh-7.3.3/lib/net/ssh/transport/server_version.rb +77 -0
  108. data/net-ssh-7.3.3/lib/net/ssh/transport/session.rb +354 -0
  109. data/net-ssh-7.3.3/lib/net/ssh/transport/state.rb +208 -0
  110. data/net-ssh-7.3.3/lib/net/ssh/verifiers/accept_new.rb +33 -0
  111. data/net-ssh-7.3.3/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +33 -0
  112. data/net-ssh-7.3.3/lib/net/ssh/verifiers/always.rb +58 -0
  113. data/net-ssh-7.3.3/lib/net/ssh/verifiers/never.rb +19 -0
  114. data/net-ssh-7.3.3/lib/net/ssh/version.rb +68 -0
  115. data/net-ssh-7.3.3/lib/net/ssh.rb +334 -0
  116. data/net-ssh-7.3.3/net-ssh-public_cert.pem +21 -0
  117. data/net-ssh-7.3.3/net-ssh.gemspec +48 -0
  118. data/net-ssh-7.3.3/support/ssh_tunnel_bug.rb +65 -0
  119. data/super-fast-gem.gemspec +12 -0
  120. metadata +159 -0
@@ -0,0 +1,342 @@
1
+ require 'net/ssh/errors'
2
+ require 'net/ssh/key_factory'
3
+ require 'net/ssh/loggable'
4
+ require 'net/ssh/authentication/agent'
5
+
6
+ module Net
7
+ module SSH
8
+ module Authentication
9
+ # A trivial exception class used to report errors in the key manager.
10
+ class KeyManagerError < Net::SSH::Exception; end
11
+
12
+ # This class encapsulates all operations done by clients on a user's
13
+ # private keys. In practice, the client should never need a reference
14
+ # to a private key; instead, they grab a list of "identities" (public
15
+ # keys) that are available from the KeyManager, and then use
16
+ # the KeyManager to do various private key operations using those
17
+ # identities.
18
+ #
19
+ # The KeyManager also uses the Agent class to encapsulate the
20
+ # ssh-agent. Thus, from a client's perspective it is completely
21
+ # hidden whether an identity comes from the ssh-agent or from a file
22
+ # on disk.
23
+ class KeyManager
24
+ include Loggable
25
+
26
+ # The list of user key files that will be examined
27
+ attr_reader :key_files
28
+
29
+ # The list of user key data that will be examined
30
+ attr_reader :key_data
31
+
32
+ # The list of user key certificate files that will be examined
33
+ attr_reader :keycert_files
34
+
35
+ # The list of user key certificate data that will be examined
36
+ attr_reader :keycert_data
37
+
38
+ # The map of loaded identities
39
+ attr_reader :known_identities
40
+
41
+ # The map of options that were passed to the key-manager
42
+ attr_reader :options
43
+
44
+ # Create a new KeyManager. By default, the manager will
45
+ # use the ssh-agent if it is running and the `:use_agent` option
46
+ # is not false.
47
+ def initialize(logger, options = {})
48
+ self.logger = logger
49
+ @key_files = []
50
+ @key_data = []
51
+ @keycert_files = []
52
+ @keycert_data = []
53
+ @use_agent = options[:use_agent] != false
54
+ @known_identities = {}
55
+ @agent = nil
56
+ @options = options
57
+ end
58
+
59
+ # Clear all knowledge of any loaded user keys. This also clears the list
60
+ # of default identity files that are to be loaded, thus making it
61
+ # appropriate to use if a client wishes to NOT use the default identity
62
+ # files.
63
+ def clear!
64
+ key_files.clear
65
+ key_data.clear
66
+ keycert_data.clear
67
+ known_identities.clear
68
+ self
69
+ end
70
+
71
+ # Add the given key_file to the list of key files that will be used.
72
+ def add(key_file)
73
+ key_files.push(File.expand_path(key_file)).uniq!
74
+ self
75
+ rescue ArgumentError
76
+ self
77
+ end
78
+
79
+ # Add the given keycert_file to the list of keycert files that will be used.
80
+ def add_keycert(keycert_file)
81
+ keycert_files.push(File.expand_path(keycert_file)).uniq!
82
+ self
83
+ rescue ArgumentError
84
+ self
85
+ end
86
+
87
+ # Add the given keycert_data to the list of keycerts that will be used.
88
+ def add_keycert_data(keycert_data_)
89
+ keycert_data.push(keycert_data_).uniq!
90
+ self
91
+ end
92
+
93
+ # Add the given key_file to the list of keys that will be used.
94
+ def add_key_data(key_data_)
95
+ key_data.push(key_data_).uniq!
96
+ self
97
+ end
98
+
99
+ # This is used as a hint to the KeyManager indicating that the agent
100
+ # connection is no longer needed. Any other open resources may be closed
101
+ # at this time.
102
+ #
103
+ # Calling this does NOT indicate that the KeyManager will no longer
104
+ # be used. Identities may still be requested and operations done on
105
+ # loaded identities, in which case, the agent will be automatically
106
+ # reconnected. This method simply allows the client connection to be
107
+ # closed when it will not be used in the immediate future.
108
+ def finish
109
+ @agent.close if @agent
110
+ @agent = nil
111
+ end
112
+
113
+ # Iterates over all available identities (public keys) known to this
114
+ # manager. As it finds one, it will then yield it to the caller.
115
+ # The origin of the identities may be from files on disk or from an
116
+ # ssh-agent. Note that identities from an ssh-agent are always listed
117
+ # first in the array, with other identities coming after.
118
+ #
119
+ # If key manager was created with :keys_only option, any identity
120
+ # from ssh-agent will be ignored unless it present in key_files or
121
+ # key_data.
122
+ def each_identity
123
+ prepared_identities = prepare_identities_from_files + prepare_identities_from_data
124
+
125
+ user_identities = load_identities(prepared_identities, false, true)
126
+
127
+ if agent
128
+ agent.identities.each do |key|
129
+ corresponding_user_identity = user_identities.detect { |identity|
130
+ identity[:public_key] && identity[:public_key].to_pem == key.to_pem
131
+ }
132
+ user_identities.delete(corresponding_user_identity) if corresponding_user_identity
133
+
134
+ if !options[:keys_only] || corresponding_user_identity
135
+ known_identities[key] = { from: :agent, identity: key }
136
+ yield key
137
+ end
138
+ end
139
+ end
140
+
141
+ user_identities = load_identities(user_identities, !options[:non_interactive], false)
142
+
143
+ user_identities.each do |identity|
144
+ key = identity.delete(:public_key)
145
+ known_identities[key] = identity
146
+ yield key
147
+ end
148
+
149
+ known_identity_blobs = known_identities.keys.map(&:to_blob)
150
+
151
+ keycerts.each do |keycert|
152
+ next if known_identity_blobs.include?(keycert.to_blob)
153
+
154
+ (_, corresponding_identity) = known_identities.detect { |public_key, _|
155
+ public_key.to_pem == keycert.to_pem
156
+ }
157
+
158
+ if corresponding_identity
159
+ known_identities[keycert] = corresponding_identity
160
+ yield keycert
161
+ end
162
+ end
163
+
164
+ self
165
+ end
166
+
167
+ # Sign the given data, using the corresponding private key of the given
168
+ # identity. If the identity was originally obtained from an ssh-agent,
169
+ # then the ssh-agent will be used to sign the data, otherwise the
170
+ # private key for the identity will be loaded from disk (if it hasn't
171
+ # been loaded already) and will then be used to sign the data.
172
+ #
173
+ # Regardless of the identity's origin or who does the signing, this
174
+ # will always return the signature in an SSH2-specified "signature
175
+ # blob" format.
176
+ def sign(identity, data, sig_alg = nil)
177
+ info = known_identities[identity] or raise KeyManagerError, "the given identity is unknown to the key manager"
178
+
179
+ if info[:from] == :pubkey_file_only
180
+ raise KeyManagerError, "the given identity is a public key only and cannot be used for signing without an agent"
181
+ end
182
+
183
+ if info[:key].nil? && (info[:from] == :file || info[:from] == :pubkey_file)
184
+ begin
185
+ info[:key] = KeyFactory.load_private_key(info[:file], options[:passphrase], !options[:non_interactive], options[:password_prompt])
186
+ rescue OpenSSL::OpenSSLError, Exception => e
187
+ raise KeyManagerError, "the given identity is known, but the private key could not be loaded: #{e.class} (#{e.message})"
188
+ end
189
+ end
190
+
191
+ if info[:key]
192
+ if sig_alg.nil?
193
+ signed = info[:key].ssh_do_sign(data.to_s)
194
+ sig_alg = identity.ssh_signature_type
195
+ else
196
+ signed = info[:key].ssh_do_sign(data.to_s, sig_alg)
197
+ end
198
+ return Net::SSH::Buffer.from(:string, sig_alg,
199
+ :mstring, signed).to_s
200
+ end
201
+
202
+ if info[:from] == :agent
203
+ raise KeyManagerError, "the agent is no longer available" unless agent
204
+
205
+ case sig_alg
206
+ when "rsa-sha2-512"
207
+ return agent.sign(info[:identity], data.to_s, Net::SSH::Authentication::Agent::SSH_AGENT_RSA_SHA2_512)
208
+ when "rsa-sha2-256"
209
+ return agent.sign(info[:identity], data.to_s, Net::SSH::Authentication::Agent::SSH_AGENT_RSA_SHA2_256)
210
+ else
211
+ return agent.sign(info[:identity], data.to_s)
212
+ end
213
+ end
214
+
215
+ raise KeyManagerError, "[BUG] can't determine identity origin (#{info.inspect})"
216
+ end
217
+
218
+ # Identifies whether the ssh-agent will be used or not.
219
+ def use_agent?
220
+ @use_agent
221
+ end
222
+
223
+ # Toggles whether the ssh-agent will be used or not. If true, an
224
+ # attempt will be made to use the ssh-agent. If false, any existing
225
+ # connection to an agent is closed and the agent will not be used.
226
+ def use_agent=(use_agent)
227
+ finish if !use_agent
228
+ @use_agent = use_agent
229
+ end
230
+
231
+ # Returns an Agent instance to use for communicating with an SSH
232
+ # agent process. Returns nil if use of an SSH agent has been disabled,
233
+ # or if the agent is otherwise not available.
234
+ def agent
235
+ return unless use_agent?
236
+
237
+ @agent ||= Agent.connect(logger, options[:agent_socket_factory], options[:identity_agent])
238
+ rescue AgentNotAvailable
239
+ @use_agent = false
240
+ nil
241
+ end
242
+
243
+ def no_keys?
244
+ key_files.empty? && key_data.empty?
245
+ end
246
+
247
+ private
248
+
249
+ # Load keycerts from files and data.
250
+ def keycerts
251
+ keycert_files.map { |keycert_file| KeyFactory.load_public_key(keycert_file) } +
252
+ keycert_data.map { |data| KeyFactory.load_data_public_key(data) }
253
+ end
254
+
255
+ # Prepares identities from user key_files for loading, preserving their order and sources.
256
+ def prepare_identities_from_files
257
+ key_files.map do |file|
258
+ if readable_file?(file)
259
+ identity = { privkey_file: file }
260
+ cert_file = file + "-cert.pub"
261
+ public_key_file = file + ".pub"
262
+ if file.end_with?(".pub")
263
+ identity[:load_from] = :pubkey_file_only
264
+ identity[:pubkey_file] = file
265
+ identity.delete(:privkey_file)
266
+ elsif readable_file?(cert_file)
267
+ identity[:load_from] = :pubkey_file
268
+ identity[:pubkey_file] = cert_file
269
+ elsif readable_file?(public_key_file)
270
+ identity[:load_from] = :pubkey_file
271
+ identity[:pubkey_file] = public_key_file
272
+ else
273
+ identity[:load_from] = :privkey_file
274
+ end
275
+ identity
276
+ end
277
+ end.compact
278
+ end
279
+
280
+ def readable_file?(path)
281
+ File.file?(path) && File.readable?(path)
282
+ end
283
+
284
+ # Prepared identities from user key_data, preserving their order and sources.
285
+ def prepare_identities_from_data
286
+ key_data.map do |data|
287
+ { load_from: :data, data: data }
288
+ end
289
+ end
290
+
291
+ # Load prepared identities. Private key decryption errors ignored if ignore_decryption_errors
292
+ def load_identities(identities, ask_passphrase, ignore_decryption_errors)
293
+ identities.map do |identity|
294
+ case identity[:load_from]
295
+ when :pubkey_file
296
+ key = KeyFactory.load_public_key(identity[:pubkey_file])
297
+ { public_key: key, from: :pubkey_file, file: identity[:privkey_file] }
298
+ when :pubkey_file_only
299
+ key = KeyFactory.load_public_key(identity[:pubkey_file])
300
+ { public_key: key, from: :pubkey_file_only, file: identity[:privkey_file] }
301
+ when :privkey_file
302
+ private_key = KeyFactory.load_private_key(
303
+ identity[:privkey_file], options[:passphrase], ask_passphrase, options[:password_prompt]
304
+ )
305
+ key = private_key.send(:public_key)
306
+ { public_key: key, from: :file, file: identity[:privkey_file], key: private_key }
307
+ when :data
308
+ private_key = KeyFactory.load_data_private_key(
309
+ identity[:data], options[:passphrase], ask_passphrase, "<key in memory>", options[:password_prompt]
310
+ )
311
+ key = private_key.send(:public_key)
312
+ { public_key: key, from: :key_data, data: identity[:data], key: private_key }
313
+ else
314
+ identity
315
+ end
316
+ rescue OpenSSL::PKey::RSAError, OpenSSL::PKey::DSAError, OpenSSL::PKey::ECError, OpenSSL::PKey::PKeyError, ArgumentError => e
317
+ if ignore_decryption_errors
318
+ identity
319
+ else
320
+ process_identity_loading_error(identity, e)
321
+ nil
322
+ end
323
+ rescue Exception => e
324
+ process_identity_loading_error(identity, e)
325
+ nil
326
+ end.compact
327
+ end
328
+
329
+ def process_identity_loading_error(identity, e)
330
+ case identity[:load_from]
331
+ when :pubkey_file, :pubkey_file_only
332
+ error { "could not load public key file `#{identity[:pubkey_file]}': #{e.class} (#{e.message})" }
333
+ when :privkey_file
334
+ error { "could not load private key file `#{identity[:privkey_file]}': #{e.class} (#{e.message})" }
335
+ else
336
+ raise e
337
+ end
338
+ end
339
+ end
340
+ end
341
+ end
342
+ end
@@ -0,0 +1,79 @@
1
+ require 'net/ssh/buffer'
2
+ require 'net/ssh/errors'
3
+ require 'net/ssh/loggable'
4
+ require 'net/ssh/authentication/constants'
5
+
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
15
+
16
+ # The authentication session object
17
+ attr_reader :session
18
+
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
53
+
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
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,72 @@
1
+ require 'net/ssh/authentication/methods/abstract'
2
+
3
+ module Net
4
+ module SSH
5
+ module Authentication
6
+ module Methods
7
+ # Implements the host-based SSH authentication method.
8
+ class Hostbased < Abstract
9
+ include Constants
10
+
11
+ # Attempts to perform host-based authorization of the user by trying
12
+ # all known keys.
13
+ def authenticate(next_service, username, password = nil)
14
+ return false unless key_manager
15
+
16
+ key_manager.each_identity do |identity|
17
+ return true if authenticate_with(identity, next_service,
18
+ username, key_manager)
19
+ end
20
+
21
+ return false
22
+ end
23
+
24
+ private
25
+
26
+ # Returns the hostname as reported by the underlying socket.
27
+ def hostname
28
+ session.transport.socket.client_name
29
+ end
30
+
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
36
+
37
+ req = build_request(identity, next_service, username, "#{hostname}.", client_username)
38
+ sig_data = Buffer.from(:string, session_id, :raw, req)
39
+
40
+ sig = key_manager.sign(identity, sig_data.to_s)
41
+
42
+ message = Buffer.from(:raw, req, :string, sig)
43
+
44
+ send_message(message)
45
+ message = session.next_message
46
+
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})" }
53
+
54
+ raise Net::SSH::Authentication::DisallowedMethod unless
55
+ message[:authentications].split(/,/).include? 'hostbased'
56
+
57
+ return false
58
+ else
59
+ raise Net::SSH::Exception, "unexpected server response to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
60
+ end
61
+ end
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
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,77 @@
1
+ require 'net/ssh/prompt'
2
+ require 'net/ssh/authentication/methods/abstract'
3
+
4
+ module Net
5
+ module SSH
6
+ module Authentication
7
+ module Methods
8
+ # Implements the "keyboard-interactive" SSH authentication method.
9
+ class KeyboardInteractive < Abstract
10
+ USERAUTH_INFO_REQUEST = 60
11
+ USERAUTH_INFO_RESPONSE = 61
12
+
13
+ # Attempt to authenticate the given user for the given service.
14
+ def authenticate(next_service, username, password = nil)
15
+ debug { "trying keyboard-interactive" }
16
+ send_message(userauth_request(username, next_service, "keyboard-interactive", "", ""))
17
+
18
+ prompter = nil
19
+ loop do
20
+ message = session.next_message
21
+
22
+ case message.type
23
+ when USERAUTH_SUCCESS
24
+ debug { "keyboard-interactive succeeded" }
25
+ prompter.success if prompter
26
+ return true
27
+ when USERAUTH_FAILURE
28
+ debug { "keyboard-interactive failed" }
29
+
30
+ raise Net::SSH::Authentication::DisallowedMethod unless
31
+ message[:authentications].split(/,/).include? 'keyboard-interactive'
32
+
33
+ return false unless interactive?
34
+
35
+ password = nil
36
+ debug { "retrying keyboard-interactive" }
37
+ send_message(userauth_request(username, next_service, "keyboard-interactive", "", ""))
38
+ when USERAUTH_INFO_REQUEST
39
+ name = message.read_string
40
+ instruction = message.read_string
41
+ debug { "keyboard-interactive info request" }
42
+
43
+ if password.nil? && interactive? && prompter.nil?
44
+ prompter = prompt.start(type: 'keyboard-interactive', name: name, instruction: instruction)
45
+ end
46
+
47
+ _ = message.read_string # lang_tag
48
+ responses = []
49
+
50
+ message.read_long.times do
51
+ text = message.read_string
52
+ echo = message.read_bool
53
+ password_to_send = password || (prompter && prompter.ask(text, echo))
54
+ responses << password_to_send
55
+ end
56
+
57
+ # if the password failed the first time around, don't try
58
+ # and use it on subsequent requests.
59
+ password = nil
60
+
61
+ msg = Buffer.from(:byte, USERAUTH_INFO_RESPONSE, :long, responses.length, :string, responses)
62
+ send_message(msg)
63
+ else
64
+ raise Net::SSH::Exception, "unexpected reply in keyboard interactive: #{message.type} (#{message.inspect})"
65
+ end
66
+ end
67
+ end
68
+
69
+ def interactive?
70
+ options = session.transport.options || {}
71
+ !options[:non_interactive]
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,34 @@
1
+ require 'net/ssh/errors'
2
+ require 'net/ssh/authentication/methods/abstract'
3
+
4
+ module Net
5
+ module SSH
6
+ module Authentication
7
+ module Methods
8
+ # Implements the "none" SSH authentication method.
9
+ class None < Abstract
10
+ # Attempt to authenticate as "none"
11
+ def authenticate(next_service, user = "", password = "")
12
+ send_message(userauth_request(user, next_service, "none"))
13
+ message = session.next_message
14
+
15
+ case message.type
16
+ when USERAUTH_SUCCESS
17
+ debug { "none succeeded" }
18
+ return true
19
+ when USERAUTH_FAILURE
20
+ debug { "none failed" }
21
+
22
+ raise Net::SSH::Authentication::DisallowedMethod unless
23
+ message[:authentications].split(/,/).include? 'none'
24
+
25
+ return false
26
+ else
27
+ raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,80 @@
1
+ require 'net/ssh/errors'
2
+ require 'net/ssh/prompt'
3
+ require 'net/ssh/authentication/methods/abstract'
4
+
5
+ module Net
6
+ module SSH
7
+ module Authentication
8
+ module Methods
9
+ # Implements the "password" SSH authentication method.
10
+ class Password < Abstract
11
+ # Attempt to authenticate the given user for the given service. If
12
+ # the password parameter is nil, this will ask for password
13
+ def authenticate(next_service, username, password = nil)
14
+ clear_prompter!
15
+ retries = 0
16
+ max_retries = get_max_retries
17
+ return false if !password && max_retries == 0
18
+
19
+ begin
20
+ password_to_send = password || ask_password(username)
21
+
22
+ send_message(userauth_request(username, next_service, "password", false, password_to_send))
23
+ message = session.next_message
24
+ retries += 1
25
+
26
+ if message.type == USERAUTH_FAILURE
27
+ debug { "password failed" }
28
+
29
+ raise Net::SSH::Authentication::DisallowedMethod unless
30
+ message[:authentications].split(/,/).include? 'password'
31
+
32
+ password = nil
33
+ end
34
+ end until (message.type != USERAUTH_FAILURE || retries >= max_retries)
35
+
36
+ case message.type
37
+ when USERAUTH_SUCCESS
38
+ debug { "password succeeded" }
39
+ @prompter.success if @prompter
40
+ return true
41
+ when USERAUTH_FAILURE
42
+ return false
43
+ when USERAUTH_PASSWD_CHANGEREQ
44
+ debug { "password change request received, failing" }
45
+ return false
46
+ else
47
+ raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ NUMBER_OF_PASSWORD_PROMPTS = 3
54
+
55
+ def clear_prompter!
56
+ @prompt_info = nil
57
+ @prompter = nil
58
+ end
59
+
60
+ def ask_password(username)
61
+ host = session.transport.host
62
+ prompt_info = { type: 'password', user: username, host: host }
63
+ if @prompt_info != prompt_info
64
+ @prompt_info = prompt_info
65
+ @prompter = prompt.start(prompt_info)
66
+ end
67
+ echo = false
68
+ @prompter.ask("#{username}@#{host}'s password:", echo)
69
+ end
70
+
71
+ def get_max_retries
72
+ options = session.transport.options || {}
73
+ result = options[:number_of_password_prompts] || NUMBER_OF_PASSWORD_PROMPTS
74
+ options[:non_interactive] ? 0 : result
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end