net-ssh 3.2.0.rc2 → 7.1.0

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 (204) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +2 -2
  3. data/.dockerignore +6 -0
  4. data/.github/config/rubocop_linter_action.yml +4 -0
  5. data/.github/workflows/ci-with-docker.yml +44 -0
  6. data/.github/workflows/ci.yml +87 -0
  7. data/.github/workflows/rubocop.yml +16 -0
  8. data/.gitignore +13 -0
  9. data/.rubocop.yml +22 -0
  10. data/.rubocop_todo.yml +1081 -0
  11. data/CHANGES.txt +228 -7
  12. data/Dockerfile +27 -0
  13. data/Dockerfile.openssl3 +17 -0
  14. data/Gemfile +13 -0
  15. data/Gemfile.noed25519 +12 -0
  16. data/ISSUE_TEMPLATE.md +30 -0
  17. data/Manifest +4 -5
  18. data/README.md +297 -0
  19. data/Rakefile +125 -74
  20. data/SECURITY.md +4 -0
  21. data/appveyor.yml +58 -0
  22. data/docker-compose.yml +23 -0
  23. data/lib/net/ssh/authentication/agent.rb +279 -18
  24. data/lib/net/ssh/authentication/certificate.rb +183 -0
  25. data/lib/net/ssh/authentication/constants.rb +17 -15
  26. data/lib/net/ssh/authentication/ed25519.rb +186 -0
  27. data/lib/net/ssh/authentication/ed25519_loader.rb +31 -0
  28. data/lib/net/ssh/authentication/key_manager.rb +86 -39
  29. data/lib/net/ssh/authentication/methods/abstract.rb +67 -48
  30. data/lib/net/ssh/authentication/methods/hostbased.rb +34 -37
  31. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +13 -13
  32. data/lib/net/ssh/authentication/methods/none.rb +16 -19
  33. data/lib/net/ssh/authentication/methods/password.rb +27 -17
  34. data/lib/net/ssh/authentication/methods/publickey.rb +96 -55
  35. data/lib/net/ssh/authentication/pageant.rb +471 -367
  36. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +43 -0
  37. data/lib/net/ssh/authentication/session.rb +131 -121
  38. data/lib/net/ssh/buffer.rb +399 -300
  39. data/lib/net/ssh/buffered_io.rb +154 -150
  40. data/lib/net/ssh/config.rb +308 -185
  41. data/lib/net/ssh/connection/channel.rb +635 -613
  42. data/lib/net/ssh/connection/constants.rb +29 -29
  43. data/lib/net/ssh/connection/event_loop.rb +123 -0
  44. data/lib/net/ssh/connection/keepalive.rb +55 -51
  45. data/lib/net/ssh/connection/session.rb +620 -551
  46. data/lib/net/ssh/connection/term.rb +125 -123
  47. data/lib/net/ssh/errors.rb +101 -99
  48. data/lib/net/ssh/key_factory.rb +197 -105
  49. data/lib/net/ssh/known_hosts.rb +214 -127
  50. data/lib/net/ssh/loggable.rb +50 -49
  51. data/lib/net/ssh/packet.rb +83 -79
  52. data/lib/net/ssh/prompt.rb +50 -81
  53. data/lib/net/ssh/proxy/command.rb +105 -90
  54. data/lib/net/ssh/proxy/errors.rb +12 -10
  55. data/lib/net/ssh/proxy/http.rb +82 -79
  56. data/lib/net/ssh/proxy/https.rb +50 -0
  57. data/lib/net/ssh/proxy/jump.rb +54 -0
  58. data/lib/net/ssh/proxy/socks4.rb +2 -6
  59. data/lib/net/ssh/proxy/socks5.rb +14 -17
  60. data/lib/net/ssh/service/forward.rb +370 -317
  61. data/lib/net/ssh/test/channel.rb +145 -136
  62. data/lib/net/ssh/test/extensions.rb +131 -110
  63. data/lib/net/ssh/test/kex.rb +34 -32
  64. data/lib/net/ssh/test/local_packet.rb +46 -44
  65. data/lib/net/ssh/test/packet.rb +89 -70
  66. data/lib/net/ssh/test/remote_packet.rb +32 -30
  67. data/lib/net/ssh/test/script.rb +156 -142
  68. data/lib/net/ssh/test/socket.rb +49 -48
  69. data/lib/net/ssh/test.rb +82 -77
  70. data/lib/net/ssh/transport/algorithms.rb +441 -360
  71. data/lib/net/ssh/transport/cipher_factory.rb +96 -98
  72. data/lib/net/ssh/transport/constants.rb +32 -24
  73. data/lib/net/ssh/transport/ctr.rb +42 -22
  74. data/lib/net/ssh/transport/hmac/abstract.rb +81 -63
  75. data/lib/net/ssh/transport/hmac/md5.rb +0 -2
  76. data/lib/net/ssh/transport/hmac/md5_96.rb +0 -2
  77. data/lib/net/ssh/transport/hmac/none.rb +0 -2
  78. data/lib/net/ssh/transport/hmac/ripemd160.rb +0 -2
  79. data/lib/net/ssh/transport/hmac/sha1.rb +0 -2
  80. data/lib/net/ssh/transport/hmac/sha1_96.rb +0 -2
  81. data/lib/net/ssh/transport/hmac/sha2_256.rb +7 -11
  82. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +4 -8
  83. data/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
  84. data/lib/net/ssh/transport/hmac/sha2_512.rb +6 -9
  85. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +4 -8
  86. data/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
  87. data/lib/net/ssh/transport/hmac.rb +14 -12
  88. data/lib/net/ssh/transport/identity_cipher.rb +54 -52
  89. data/lib/net/ssh/transport/kex/abstract.rb +130 -0
  90. data/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
  91. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +39 -0
  92. data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
  93. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +33 -40
  94. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
  95. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +119 -213
  96. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +53 -61
  97. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +5 -9
  98. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +36 -90
  99. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +18 -10
  100. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +18 -10
  101. data/lib/net/ssh/transport/kex.rb +15 -12
  102. data/lib/net/ssh/transport/key_expander.rb +24 -20
  103. data/lib/net/ssh/transport/openssl.rb +161 -124
  104. data/lib/net/ssh/transport/packet_stream.rb +225 -185
  105. data/lib/net/ssh/transport/server_version.rb +55 -56
  106. data/lib/net/ssh/transport/session.rb +306 -255
  107. data/lib/net/ssh/transport/state.rb +178 -176
  108. data/lib/net/ssh/verifiers/accept_new.rb +33 -0
  109. data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +33 -0
  110. data/lib/net/ssh/verifiers/always.rb +58 -0
  111. data/lib/net/ssh/verifiers/never.rb +19 -0
  112. data/lib/net/ssh/version.rb +55 -53
  113. data/lib/net/ssh.rb +110 -47
  114. data/net-ssh-public_cert.pem +18 -18
  115. data/net-ssh.gemspec +36 -205
  116. data/support/ssh_tunnel_bug.rb +5 -5
  117. data.tar.gz.sig +0 -0
  118. metadata +153 -118
  119. metadata.gz.sig +0 -0
  120. data/.travis.yml +0 -18
  121. data/README.rdoc +0 -182
  122. data/lib/net/ssh/authentication/agent/java_pageant.rb +0 -85
  123. data/lib/net/ssh/authentication/agent/socket.rb +0 -178
  124. data/lib/net/ssh/ruby_compat.rb +0 -46
  125. data/lib/net/ssh/verifiers/lenient.rb +0 -30
  126. data/lib/net/ssh/verifiers/null.rb +0 -12
  127. data/lib/net/ssh/verifiers/secure.rb +0 -52
  128. data/lib/net/ssh/verifiers/strict.rb +0 -24
  129. data/setup.rb +0 -1585
  130. data/support/arcfour_check.rb +0 -20
  131. data/test/README.txt +0 -18
  132. data/test/authentication/methods/common.rb +0 -28
  133. data/test/authentication/methods/test_abstract.rb +0 -51
  134. data/test/authentication/methods/test_hostbased.rb +0 -114
  135. data/test/authentication/methods/test_keyboard_interactive.rb +0 -121
  136. data/test/authentication/methods/test_none.rb +0 -41
  137. data/test/authentication/methods/test_password.rb +0 -95
  138. data/test/authentication/methods/test_publickey.rb +0 -148
  139. data/test/authentication/test_agent.rb +0 -232
  140. data/test/authentication/test_key_manager.rb +0 -240
  141. data/test/authentication/test_session.rb +0 -107
  142. data/test/common.rb +0 -125
  143. data/test/configs/auth_off +0 -5
  144. data/test/configs/auth_on +0 -4
  145. data/test/configs/empty +0 -0
  146. data/test/configs/eqsign +0 -3
  147. data/test/configs/exact_match +0 -8
  148. data/test/configs/host_plus +0 -10
  149. data/test/configs/multihost +0 -4
  150. data/test/configs/negative_match +0 -6
  151. data/test/configs/nohost +0 -19
  152. data/test/configs/numeric_host +0 -4
  153. data/test/configs/proxy_remote_user +0 -2
  154. data/test/configs/send_env +0 -2
  155. data/test/configs/substitutes +0 -8
  156. data/test/configs/wild_cards +0 -14
  157. data/test/connection/test_channel.rb +0 -487
  158. data/test/connection/test_session.rb +0 -564
  159. data/test/integration/README.txt +0 -17
  160. data/test/integration/Vagrantfile +0 -12
  161. data/test/integration/common.rb +0 -63
  162. data/test/integration/playbook.yml +0 -56
  163. data/test/integration/test_forward.rb +0 -637
  164. data/test/integration/test_id_rsa_keys.rb +0 -96
  165. data/test/integration/test_proxy.rb +0 -93
  166. data/test/known_hosts/github +0 -1
  167. data/test/known_hosts/github_hash +0 -1
  168. data/test/manual/test_pageant.rb +0 -37
  169. data/test/start/test_connection.rb +0 -53
  170. data/test/start/test_options.rb +0 -57
  171. data/test/start/test_transport.rb +0 -28
  172. data/test/start/test_user_nil.rb +0 -27
  173. data/test/test_all.rb +0 -12
  174. data/test/test_buffer.rb +0 -433
  175. data/test/test_buffered_io.rb +0 -63
  176. data/test/test_config.rb +0 -268
  177. data/test/test_key_factory.rb +0 -191
  178. data/test/test_known_hosts.rb +0 -66
  179. data/test/transport/hmac/test_md5.rb +0 -41
  180. data/test/transport/hmac/test_md5_96.rb +0 -27
  181. data/test/transport/hmac/test_none.rb +0 -34
  182. data/test/transport/hmac/test_ripemd160.rb +0 -36
  183. data/test/transport/hmac/test_sha1.rb +0 -36
  184. data/test/transport/hmac/test_sha1_96.rb +0 -27
  185. data/test/transport/hmac/test_sha2_256.rb +0 -37
  186. data/test/transport/hmac/test_sha2_256_96.rb +0 -27
  187. data/test/transport/hmac/test_sha2_512.rb +0 -37
  188. data/test/transport/hmac/test_sha2_512_96.rb +0 -27
  189. data/test/transport/kex/test_diffie_hellman_group14_sha1.rb +0 -13
  190. data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +0 -150
  191. data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +0 -96
  192. data/test/transport/kex/test_diffie_hellman_group_exchange_sha256.rb +0 -19
  193. data/test/transport/kex/test_ecdh_sha2_nistp256.rb +0 -161
  194. data/test/transport/kex/test_ecdh_sha2_nistp384.rb +0 -38
  195. data/test/transport/kex/test_ecdh_sha2_nistp521.rb +0 -38
  196. data/test/transport/test_algorithms.rb +0 -328
  197. data/test/transport/test_cipher_factory.rb +0 -443
  198. data/test/transport/test_hmac.rb +0 -34
  199. data/test/transport/test_identity_cipher.rb +0 -40
  200. data/test/transport/test_packet_stream.rb +0 -1762
  201. data/test/transport/test_server_version.rb +0 -74
  202. data/test/transport/test_session.rb +0 -331
  203. data/test/transport/test_state.rb +0 -181
  204. data/test/verifiers/test_secure.rb +0 -40
@@ -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
@@ -5,25 +5,24 @@ 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
- include Prompt
12
-
13
10
  USERAUTH_INFO_REQUEST = 60
14
11
  USERAUTH_INFO_RESPONSE = 61
15
12
 
16
13
  # Attempt to authenticate the given user for the given service.
17
- def authenticate(next_service, username, password=nil)
14
+ def authenticate(next_service, username, password = nil)
18
15
  debug { "trying keyboard-interactive" }
19
16
  send_message(userauth_request(username, next_service, "keyboard-interactive", "", ""))
20
17
 
18
+ prompter = nil
21
19
  loop do
22
20
  message = session.next_message
23
21
 
24
22
  case message.type
25
23
  when USERAUTH_SUCCESS
26
24
  debug { "keyboard-interactive succeeded" }
25
+ prompter.success if prompter
27
26
  return true
28
27
  when USERAUTH_FAILURE
29
28
  debug { "keyboard-interactive failed" }
@@ -31,26 +30,27 @@ module Net
31
30
  raise Net::SSH::Authentication::DisallowedMethod unless
32
31
  message[:authentications].split(/,/).include? 'keyboard-interactive'
33
32
 
34
- return false
33
+ return false unless interactive?
34
+
35
+ password = nil
36
+ debug { "retrying keyboard-interactive" }
37
+ send_message(userauth_request(username, next_service, "keyboard-interactive", "", ""))
35
38
  when USERAUTH_INFO_REQUEST
36
39
  name = message.read_string
37
40
  instruction = message.read_string
38
41
  debug { "keyboard-interactive info request" }
39
42
 
40
- unless password
41
- if interactive?
42
- puts(name) unless name.empty?
43
- puts(instruction) unless instruction.empty?
44
- end
43
+ if password.nil? && interactive? && prompter.nil?
44
+ prompter = prompt.start(type: 'keyboard-interactive', name: name, instruction: instruction)
45
45
  end
46
46
 
47
47
  _ = message.read_string # lang_tag
48
- responses =[]
49
-
48
+ responses = []
49
+
50
50
  message.read_long.times do
51
51
  text = message.read_string
52
52
  echo = message.read_bool
53
- password_to_send = password || (interactive? ? prompt(text, echo) : nil)
53
+ password_to_send = password || (prompter && prompter.ask(text, echo))
54
54
  responses << password_to_send
55
55
  end
56
56
 
@@ -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
- when USERAUTH_SUCCESS
18
- debug { "none succeeded" }
19
- return true
20
- when USERAUTH_FAILURE
21
- debug { "none failed" }
22
-
23
- raise Net::SSH::Authentication::DisallowedMethod unless
24
- message[:authentications].split(/,/).include? 'none'
25
-
26
- return false
27
- else
28
- raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
29
- end
30
-
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
31
29
  end
32
30
  end
33
-
34
31
  end
35
32
  end
36
33
  end
@@ -6,16 +6,14 @@ 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
- include Prompt
13
-
14
11
  # Attempt to authenticate the given user for the given service. If
15
12
  # the password parameter is nil, this will ask for password
16
- def authenticate(next_service, username, password=nil)
13
+ def authenticate(next_service, username, password = nil)
14
+ clear_prompter!
17
15
  retries = 0
18
- max_retries = get_max_retries
16
+ max_retries = get_max_retries
19
17
  return false if !password && max_retries == 0
20
18
 
21
19
  begin
@@ -30,21 +28,23 @@ module Net
30
28
 
31
29
  raise Net::SSH::Authentication::DisallowedMethod unless
32
30
  message[:authentications].split(/,/).include? 'password'
31
+
33
32
  password = nil
34
33
  end
35
34
  end until (message.type != USERAUTH_FAILURE || retries >= max_retries)
36
35
 
37
36
  case message.type
38
- when USERAUTH_SUCCESS
39
- debug { "password succeeded" }
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})"
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
48
  end
49
49
  end
50
50
 
@@ -52,9 +52,20 @@ module Net
52
52
 
53
53
  NUMBER_OF_PASSWORD_PROMPTS = 3
54
54
 
55
+ def clear_prompter!
56
+ @prompt_info = nil
57
+ @prompter = nil
58
+ end
59
+
55
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
56
67
  echo = false
57
- prompt("#{username}@#{session.transport.host}'s password:", echo)
68
+ @prompter.ask("#{username}@#{host}'s password:", echo)
58
69
  end
59
70
 
60
71
  def get_max_retries
@@ -63,7 +74,6 @@ module Net
63
74
  options[:non_interactive] ? 0 : result
64
75
  end
65
76
  end
66
-
67
77
  end
68
78
  end
69
79
  end
@@ -6,14 +6,13 @@ module Net
6
6
  module SSH
7
7
  module Authentication
8
8
  module Methods
9
-
10
9
  # Implements the "publickey" SSH authentication method.
11
10
  class Publickey < Abstract
12
11
  # Attempts to perform public-key authentication for the given
13
12
  # username, trying each identity known to the key manager. If any of
14
13
  # them succeed, returns +true+, otherwise returns +false+. This
15
14
  # requires the presence of a key manager.
16
- def authenticate(next_service, username, password=nil)
15
+ def authenticate(next_service, username, password = nil)
17
16
  return false unless key_manager
18
17
 
19
18
  key_manager.each_identity do |identity|
@@ -25,71 +24,113 @@ module Net
25
24
 
26
25
  private
27
26
 
28
- # Builds a packet that contains the request formatted for sending
29
- # a public-key request to the server.
30
- def build_request(pub_key, username, next_service, has_sig)
31
- blob = Net::SSH::Buffer.new
32
- blob.write_key pub_key
27
+ # Builds a packet that contains the request formatted for sending
28
+ # a public-key request to the server.
29
+ def build_request(pub_key, username, next_service, alg, has_sig)
30
+ blob = Net::SSH::Buffer.new
31
+ blob.write_key pub_key
33
32
 
34
- userauth_request(username, next_service, "publickey", has_sig,
35
- pub_key.ssh_type, blob.to_s)
36
- end
33
+ userauth_request(username, next_service, "publickey", has_sig,
34
+ alg, blob.to_s)
35
+ end
37
36
 
38
- # Builds and sends a request formatted for a public-key
39
- # authentication request.
40
- def send_request(pub_key, username, next_service, signature=nil)
41
- msg = build_request(pub_key, username, next_service, !signature.nil?)
42
- msg.write_string(signature) if signature
43
- send_message(msg)
44
- end
37
+ # Builds and sends a request formatted for a public-key
38
+ # authentication request.
39
+ def send_request(pub_key, username, next_service, alg, signature = nil)
40
+ msg = build_request(pub_key, username, next_service, alg,
41
+ !signature.nil?)
42
+ msg.write_string(signature) if signature
43
+ send_message(msg)
44
+ end
45
+
46
+ def authenticate_with_alg(identity, next_service, username, alg, sig_alg = nil)
47
+ debug { "trying publickey (#{identity.fingerprint})" }
48
+ send_request(identity, username, next_service, alg)
45
49
 
46
- # Attempts to perform public-key authentication for the given
47
- # username, with the given identity (public key). Returns +true+ if
48
- # successful, or +false+ otherwise.
49
- def authenticate_with(identity, next_service, username)
50
- debug { "trying publickey (#{identity.fingerprint})" }
51
- send_request(identity, username, next_service)
50
+ message = session.next_message
52
51
 
52
+ case message.type
53
+ when USERAUTH_PK_OK
54
+ buffer = build_request(identity, username, next_service, alg,
55
+ true)
56
+ sig_data = Net::SSH::Buffer.new
57
+ sig_data.write_string(session_id)
58
+ sig_data.append(buffer.to_s)
59
+
60
+ sig_blob = key_manager.sign(identity, sig_data, sig_alg)
61
+
62
+ send_request(identity, username, next_service, alg, sig_blob.to_s)
53
63
  message = session.next_message
54
64
 
55
65
  case message.type
56
- when USERAUTH_PK_OK
57
- buffer = build_request(identity, username, next_service, true)
58
- sig_data = Net::SSH::Buffer.new
59
- sig_data.write_string(session_id)
60
- sig_data.append(buffer.to_s)
61
-
62
- sig_blob = key_manager.sign(identity, sig_data)
63
-
64
- send_request(identity, username, next_service, sig_blob.to_s)
65
- message = session.next_message
66
-
67
- case message.type
68
- when USERAUTH_SUCCESS
69
- debug { "publickey succeeded (#{identity.fingerprint})" }
70
- return true
71
- when USERAUTH_FAILURE
72
- debug { "publickey failed (#{identity.fingerprint})" }
73
-
74
- raise Net::SSH::Authentication::DisallowedMethod unless
75
- message[:authentications].split(/,/).include? 'publickey'
76
-
77
- return false
78
- else
79
- raise Net::SSH::Exception,
80
- "unexpected server response to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
81
- end
66
+ when USERAUTH_SUCCESS
67
+ debug { "publickey succeeded (#{identity.fingerprint})" }
68
+ return true
69
+ when USERAUTH_FAILURE
70
+ debug { "publickey failed (#{identity.fingerprint})" }
71
+
72
+ raise Net::SSH::Authentication::DisallowedMethod unless
73
+ message[:authentications].split(/,/).include? 'publickey'
74
+
75
+ return false
76
+ else
77
+ raise Net::SSH::Exception,
78
+ "unexpected server response to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
79
+ end
82
80
 
83
- when USERAUTH_FAILURE
84
- return false
81
+ when USERAUTH_FAILURE
82
+ return false
83
+ when USERAUTH_SUCCESS
84
+ return true
85
85
 
86
- else
87
- raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
88
- end
86
+ else
87
+ raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
89
88
  end
89
+ end
90
90
 
91
+ # Attempts to perform public-key authentication for the given
92
+ # username, with the given identity (public key). Returns +true+ if
93
+ # successful, or +false+ otherwise.
94
+ def authenticate_with(identity, next_service, username)
95
+ type = identity.ssh_type
96
+ if type == "ssh-rsa"
97
+ pubkey_algorithms.each do |pk_alg|
98
+ case pk_alg
99
+ when "rsa-sha2-512", "rsa-sha2-256", "ssh-rsa"
100
+ if authenticate_with_alg(identity, next_service, username, pk_alg, pk_alg)
101
+ # success
102
+ return true
103
+ end
104
+ end
105
+ end
106
+ elsif type == "ssh-rsa-cert-v01@openssh.com"
107
+ pubkey_algorithms.each do |pk_alg|
108
+ case pk_alg
109
+ when "rsa-sha2-512-cert-v01@openssh.com"
110
+ if authenticate_with_alg(identity, next_service, username, pk_alg, "rsa-sha2-512")
111
+ # success
112
+ return true
113
+ end
114
+ when "rsa-sha2-256-cert-v01@openssh.com"
115
+ if authenticate_with_alg(identity, next_service, username, pk_alg, "rsa-sha2-256")
116
+ # success
117
+ return true
118
+ end
119
+ when "ssh-rsa-cert-v01@openssh.com"
120
+ if authenticate_with_alg(identity, next_service, username, pk_alg)
121
+ # success
122
+ return true
123
+ end
124
+ end
125
+ end
126
+ elsif authenticate_with_alg(identity, next_service, username, type)
127
+ # success
128
+ return true
129
+ end
130
+ # failure
131
+ return false
132
+ end
91
133
  end
92
-
93
134
  end
94
135
  end
95
136
  end