net-ssh 2.7.0 → 7.3.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 (199) hide show
  1. checksums.yaml +7 -0
  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 +94 -0
  8. data/.github/workflows/rubocop.yml +16 -0
  9. data/.gitignore +15 -0
  10. data/.rubocop.yml +22 -0
  11. data/.rubocop_todo.yml +1081 -0
  12. data/CHANGES.txt +387 -0
  13. data/DEVELOPMENT.md +23 -0
  14. data/Dockerfile +29 -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 +303 -0
  22. data/Rakefile +174 -40
  23. data/SECURITY.md +4 -0
  24. data/THANKS.txt +25 -0
  25. data/appveyor.yml +58 -0
  26. data/docker-compose.yml +25 -0
  27. data/lib/net/ssh/authentication/agent.rb +279 -18
  28. data/lib/net/ssh/authentication/certificate.rb +183 -0
  29. data/lib/net/ssh/authentication/constants.rb +17 -15
  30. data/lib/net/ssh/authentication/ed25519.rb +184 -0
  31. data/lib/net/ssh/authentication/ed25519_loader.rb +31 -0
  32. data/lib/net/ssh/authentication/key_manager.rb +125 -54
  33. data/lib/net/ssh/authentication/methods/abstract.rb +67 -48
  34. data/lib/net/ssh/authentication/methods/hostbased.rb +34 -37
  35. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +19 -12
  36. data/lib/net/ssh/authentication/methods/none.rb +16 -19
  37. data/lib/net/ssh/authentication/methods/password.rb +56 -19
  38. data/lib/net/ssh/authentication/methods/publickey.rb +96 -55
  39. data/lib/net/ssh/authentication/pageant.rb +483 -246
  40. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +43 -0
  41. data/lib/net/ssh/authentication/session.rb +138 -120
  42. data/lib/net/ssh/buffer.rb +399 -300
  43. data/lib/net/ssh/buffered_io.rb +154 -150
  44. data/lib/net/ssh/config.rb +361 -166
  45. data/lib/net/ssh/connection/channel.rb +640 -596
  46. data/lib/net/ssh/connection/constants.rb +29 -29
  47. data/lib/net/ssh/connection/event_loop.rb +123 -0
  48. data/lib/net/ssh/connection/keepalive.rb +59 -0
  49. data/lib/net/ssh/connection/session.rb +628 -548
  50. data/lib/net/ssh/connection/term.rb +125 -123
  51. data/lib/net/ssh/errors.rb +101 -95
  52. data/lib/net/ssh/key_factory.rb +198 -100
  53. data/lib/net/ssh/known_hosts.rb +221 -98
  54. data/lib/net/ssh/loggable.rb +50 -49
  55. data/lib/net/ssh/packet.rb +83 -79
  56. data/lib/net/ssh/prompt.rb +50 -81
  57. data/lib/net/ssh/proxy/command.rb +108 -60
  58. data/lib/net/ssh/proxy/errors.rb +12 -10
  59. data/lib/net/ssh/proxy/http.rb +82 -78
  60. data/lib/net/ssh/proxy/https.rb +50 -0
  61. data/lib/net/ssh/proxy/jump.rb +54 -0
  62. data/lib/net/ssh/proxy/socks4.rb +5 -8
  63. data/lib/net/ssh/proxy/socks5.rb +18 -20
  64. data/lib/net/ssh/service/forward.rb +383 -255
  65. data/lib/net/ssh/test/channel.rb +145 -136
  66. data/lib/net/ssh/test/extensions.rb +131 -110
  67. data/lib/net/ssh/test/kex.rb +34 -32
  68. data/lib/net/ssh/test/local_packet.rb +46 -44
  69. data/lib/net/ssh/test/packet.rb +89 -70
  70. data/lib/net/ssh/test/remote_packet.rb +32 -30
  71. data/lib/net/ssh/test/script.rb +156 -142
  72. data/lib/net/ssh/test/socket.rb +49 -48
  73. data/lib/net/ssh/test.rb +82 -77
  74. data/lib/net/ssh/transport/aes128_gcm.rb +40 -0
  75. data/lib/net/ssh/transport/aes256_gcm.rb +40 -0
  76. data/lib/net/ssh/transport/algorithms.rb +472 -348
  77. data/lib/net/ssh/transport/chacha20_poly1305_cipher.rb +117 -0
  78. data/lib/net/ssh/transport/chacha20_poly1305_cipher_loader.rb +17 -0
  79. data/lib/net/ssh/transport/cipher_factory.rb +124 -100
  80. data/lib/net/ssh/transport/constants.rb +32 -24
  81. data/lib/net/ssh/transport/ctr.rb +42 -22
  82. data/lib/net/ssh/transport/gcm_cipher.rb +207 -0
  83. data/lib/net/ssh/transport/hmac/abstract.rb +97 -63
  84. data/lib/net/ssh/transport/hmac/md5.rb +0 -2
  85. data/lib/net/ssh/transport/hmac/md5_96.rb +0 -2
  86. data/lib/net/ssh/transport/hmac/none.rb +0 -2
  87. data/lib/net/ssh/transport/hmac/ripemd160.rb +0 -2
  88. data/lib/net/ssh/transport/hmac/sha1.rb +0 -2
  89. data/lib/net/ssh/transport/hmac/sha1_96.rb +0 -2
  90. data/lib/net/ssh/transport/hmac/sha2_256.rb +7 -11
  91. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +4 -8
  92. data/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
  93. data/lib/net/ssh/transport/hmac/sha2_512.rb +6 -9
  94. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +4 -8
  95. data/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
  96. data/lib/net/ssh/transport/hmac.rb +14 -12
  97. data/lib/net/ssh/transport/identity_cipher.rb +54 -44
  98. data/lib/net/ssh/transport/kex/abstract.rb +130 -0
  99. data/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
  100. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +39 -0
  101. data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
  102. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +33 -40
  103. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
  104. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +119 -213
  105. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +53 -61
  106. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +5 -9
  107. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +36 -90
  108. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +18 -10
  109. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +18 -10
  110. data/lib/net/ssh/transport/kex.rb +15 -12
  111. data/lib/net/ssh/transport/key_expander.rb +24 -20
  112. data/lib/net/ssh/transport/openssl.rb +161 -124
  113. data/lib/net/ssh/transport/openssl_cipher_extensions.rb +8 -0
  114. data/lib/net/ssh/transport/packet_stream.rb +246 -183
  115. data/lib/net/ssh/transport/server_version.rb +57 -51
  116. data/lib/net/ssh/transport/session.rb +307 -235
  117. data/lib/net/ssh/transport/state.rb +178 -176
  118. data/lib/net/ssh/verifiers/accept_new.rb +33 -0
  119. data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +33 -0
  120. data/lib/net/ssh/verifiers/always.rb +58 -0
  121. data/lib/net/ssh/verifiers/never.rb +19 -0
  122. data/lib/net/ssh/version.rb +57 -51
  123. data/lib/net/ssh.rb +140 -40
  124. data/net-ssh-public_cert.pem +21 -0
  125. data/net-ssh.gemspec +39 -184
  126. data/support/ssh_tunnel_bug.rb +5 -5
  127. data.tar.gz.sig +0 -0
  128. metadata +205 -99
  129. metadata.gz.sig +0 -0
  130. data/README.rdoc +0 -219
  131. data/Rudyfile +0 -96
  132. data/gem-public_cert.pem +0 -20
  133. data/lib/net/ssh/authentication/agent/java_pageant.rb +0 -85
  134. data/lib/net/ssh/authentication/agent/socket.rb +0 -170
  135. data/lib/net/ssh/ruby_compat.rb +0 -51
  136. data/lib/net/ssh/verifiers/lenient.rb +0 -30
  137. data/lib/net/ssh/verifiers/null.rb +0 -12
  138. data/lib/net/ssh/verifiers/secure.rb +0 -54
  139. data/lib/net/ssh/verifiers/strict.rb +0 -24
  140. data/setup.rb +0 -1585
  141. data/support/arcfour_check.rb +0 -20
  142. data/test/README.txt +0 -47
  143. data/test/authentication/methods/common.rb +0 -28
  144. data/test/authentication/methods/test_abstract.rb +0 -51
  145. data/test/authentication/methods/test_hostbased.rb +0 -114
  146. data/test/authentication/methods/test_keyboard_interactive.rb +0 -100
  147. data/test/authentication/methods/test_none.rb +0 -41
  148. data/test/authentication/methods/test_password.rb +0 -52
  149. data/test/authentication/methods/test_publickey.rb +0 -148
  150. data/test/authentication/test_agent.rb +0 -205
  151. data/test/authentication/test_key_manager.rb +0 -218
  152. data/test/authentication/test_session.rb +0 -108
  153. data/test/common.rb +0 -108
  154. data/test/configs/eqsign +0 -3
  155. data/test/configs/exact_match +0 -8
  156. data/test/configs/host_plus +0 -10
  157. data/test/configs/multihost +0 -4
  158. data/test/configs/nohost +0 -19
  159. data/test/configs/numeric_host +0 -4
  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 -467
  164. data/test/connection/test_session.rb +0 -526
  165. data/test/known_hosts/github +0 -1
  166. data/test/manual/test_forward.rb +0 -223
  167. data/test/start/test_options.rb +0 -36
  168. data/test/start/test_transport.rb +0 -28
  169. data/test/test_all.rb +0 -11
  170. data/test/test_buffer.rb +0 -433
  171. data/test/test_buffered_io.rb +0 -63
  172. data/test/test_config.rb +0 -151
  173. data/test/test_key_factory.rb +0 -173
  174. data/test/test_known_hosts.rb +0 -13
  175. data/test/transport/hmac/test_md5.rb +0 -41
  176. data/test/transport/hmac/test_md5_96.rb +0 -27
  177. data/test/transport/hmac/test_none.rb +0 -34
  178. data/test/transport/hmac/test_ripemd160.rb +0 -36
  179. data/test/transport/hmac/test_sha1.rb +0 -36
  180. data/test/transport/hmac/test_sha1_96.rb +0 -27
  181. data/test/transport/hmac/test_sha2_256.rb +0 -37
  182. data/test/transport/hmac/test_sha2_256_96.rb +0 -27
  183. data/test/transport/hmac/test_sha2_512.rb +0 -37
  184. data/test/transport/hmac/test_sha2_512_96.rb +0 -27
  185. data/test/transport/kex/test_diffie_hellman_group14_sha1.rb +0 -13
  186. data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +0 -146
  187. data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +0 -92
  188. data/test/transport/kex/test_diffie_hellman_group_exchange_sha256.rb +0 -34
  189. data/test/transport/kex/test_ecdh_sha2_nistp256.rb +0 -161
  190. data/test/transport/kex/test_ecdh_sha2_nistp384.rb +0 -38
  191. data/test/transport/kex/test_ecdh_sha2_nistp521.rb +0 -38
  192. data/test/transport/test_algorithms.rb +0 -330
  193. data/test/transport/test_cipher_factory.rb +0 -443
  194. data/test/transport/test_hmac.rb +0 -34
  195. data/test/transport/test_identity_cipher.rb +0 -40
  196. data/test/transport/test_packet_stream.rb +0 -1755
  197. data/test/transport/test_server_version.rb +0 -78
  198. data/test/transport/test_session.rb +0 -319
  199. data/test/transport/test_state.rb +0 -181
@@ -0,0 +1,184 @@
1
+ gem 'ed25519', '~> 1.2'
2
+ gem 'bcrypt_pbkdf', '~> 1.0' unless RUBY_PLATFORM == "java"
3
+
4
+ require 'ed25519'
5
+
6
+ require 'net/ssh/transport/cipher_factory'
7
+ require 'net/ssh/authentication/pub_key_fingerprint'
8
+ require 'bcrypt_pbkdf' unless RUBY_PLATFORM == "java"
9
+
10
+ module Net
11
+ module SSH
12
+ module Authentication
13
+ module ED25519
14
+ class SigningKeyFromFile < SimpleDelegator
15
+ def initialize(pk, sk)
16
+ key = ::Ed25519::SigningKey.from_keypair(sk)
17
+ raise ArgumentError, "pk does not match sk" unless pk == key.verify_key.to_bytes
18
+
19
+ super(key)
20
+ end
21
+ end
22
+
23
+ class OpenSSHPrivateKeyLoader
24
+ CipherFactory = Net::SSH::Transport::CipherFactory
25
+
26
+ MBEGIN = "-----BEGIN OPENSSH PRIVATE KEY-----\n"
27
+ MEND = "-----END OPENSSH PRIVATE KEY-----"
28
+ MAGIC = "openssh-key-v1"
29
+
30
+ class DecryptError < ArgumentError
31
+ def initialize(message, encrypted_key: false)
32
+ super(message)
33
+ @encrypted_key = encrypted_key
34
+ end
35
+
36
+ def encrypted_key?
37
+ return @encrypted_key
38
+ end
39
+ end
40
+
41
+ def self.read(datafull, password)
42
+ datafull = datafull.strip
43
+ raise ArgumentError.new("Expected #{MBEGIN} at start of private key") unless datafull.start_with?(MBEGIN)
44
+ raise ArgumentError.new("Expected #{MEND} at end of private key") unless datafull.end_with?(MEND)
45
+
46
+ datab64 = datafull[MBEGIN.size...-MEND.size]
47
+ data = datab64.unpack1("m")
48
+ raise ArgumentError.new("Expected #{MAGIC} at start of decoded private key") unless data.start_with?(MAGIC)
49
+
50
+ buffer = Net::SSH::Buffer.new(data[MAGIC.size + 1..-1])
51
+
52
+ ciphername = buffer.read_string
53
+ raise ArgumentError.new("#{ciphername} in private key is not supported") unless
54
+ CipherFactory.supported?(ciphername)
55
+
56
+ kdfname = buffer.read_string
57
+ raise ArgumentError.new("Expected #{kdfname} to be or none or bcrypt") unless %w[none bcrypt].include?(kdfname)
58
+
59
+ kdfopts = Net::SSH::Buffer.new(buffer.read_string)
60
+ num_keys = buffer.read_long
61
+ raise ArgumentError.new("Only 1 key is supported in ssh keys #{num_keys} was in private key") unless num_keys == 1
62
+
63
+ _pubkey = buffer.read_string
64
+
65
+ len = buffer.read_long
66
+
67
+ keylen, blocksize, ivlen = CipherFactory.get_lengths(ciphername, iv_len: true)
68
+ raise ArgumentError.new("Private key len:#{len} is not a multiple of #{blocksize}") if
69
+ ((len < blocksize) || ((blocksize > 0) && (len % blocksize) != 0))
70
+
71
+ if kdfname == 'bcrypt'
72
+ salt = kdfopts.read_string
73
+ rounds = kdfopts.read_long
74
+
75
+ raise "BCryptPbkdf is not implemented for jruby" if RUBY_PLATFORM == "java"
76
+
77
+ key = BCryptPbkdf::key(password, salt, keylen + ivlen, rounds)
78
+ raise DecryptError.new("BCyryptPbkdf failed", encrypted_key: true) unless key
79
+ else
80
+ key = '\x00' * (keylen + ivlen)
81
+ end
82
+
83
+ cipher = CipherFactory.get(ciphername, key: key[0...keylen], iv: key[keylen...keylen + ivlen], decrypt: true)
84
+
85
+ decoded = cipher.update(buffer.remainder_as_buffer.to_s)
86
+ decoded << cipher.final
87
+
88
+ decoded = Net::SSH::Buffer.new(decoded)
89
+ check1 = decoded.read_long
90
+ check2 = decoded.read_long
91
+
92
+ raise DecryptError.new("Decrypt failed on private key", encrypted_key: kdfname == 'bcrypt') if (check1 != check2)
93
+
94
+ type_name = decoded.read_string
95
+ case type_name
96
+ when "ssh-ed25519"
97
+ PrivKey.new(decoded)
98
+ else
99
+ decoded.read_private_keyblob(type_name)
100
+ end
101
+ end
102
+ end
103
+
104
+ class PubKey
105
+ include Net::SSH::Authentication::PubKeyFingerprint
106
+
107
+ attr_reader :verify_key
108
+
109
+ def initialize(data)
110
+ @verify_key = ::Ed25519::VerifyKey.new(data)
111
+ end
112
+
113
+ def self.read_keyblob(buffer)
114
+ PubKey.new(buffer.read_string)
115
+ end
116
+
117
+ def to_blob
118
+ Net::SSH::Buffer.from(:mstring, "ssh-ed25519".dup, :string, @verify_key.to_bytes).to_s
119
+ end
120
+
121
+ def ssh_type
122
+ "ssh-ed25519"
123
+ end
124
+
125
+ def ssh_signature_type
126
+ ssh_type
127
+ end
128
+
129
+ def ssh_do_verify(sig, data, options = {})
130
+ @verify_key.verify(sig, data)
131
+ end
132
+
133
+ def to_pem
134
+ # TODO this is not pem
135
+ ssh_type + [@verify_key.to_bytes].pack("m")
136
+ end
137
+ end
138
+
139
+ class PrivKey
140
+ CipherFactory = Net::SSH::Transport::CipherFactory
141
+
142
+ MBEGIN = "-----BEGIN OPENSSH PRIVATE KEY-----\n"
143
+ MEND = "-----END OPENSSH PRIVATE KEY-----\n"
144
+ MAGIC = "openssh-key-v1"
145
+
146
+ attr_reader :sign_key
147
+
148
+ def initialize(buffer)
149
+ pk = buffer.read_string
150
+ sk = buffer.read_string
151
+ _comment = buffer.read_string
152
+
153
+ @pk = pk
154
+ @sign_key = SigningKeyFromFile.new(pk, sk)
155
+ end
156
+
157
+ def to_blob
158
+ public_key.to_blob
159
+ end
160
+
161
+ def ssh_type
162
+ "ssh-ed25519"
163
+ end
164
+
165
+ def ssh_signature_type
166
+ ssh_type
167
+ end
168
+
169
+ def public_key
170
+ PubKey.new(@pk)
171
+ end
172
+
173
+ def ssh_do_sign(data, sig_alg = nil)
174
+ @sign_key.sign(data)
175
+ end
176
+
177
+ def self.read(data, password)
178
+ OpenSSHPrivateKeyLoader.read(data, password)
179
+ end
180
+ end
181
+ end
182
+ end
183
+ end
184
+ 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,13 +6,12 @@ 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
 
13
12
  # This class encapsulates all operations done by clients on a user's
14
13
  # private keys. In practice, the client should never need a reference
15
- # to a private key; instead, they grab a list of "identities" (public
14
+ # to a private key; instead, they grab a list of "identities" (public
16
15
  # keys) that are available from the KeyManager, and then use
17
16
  # the KeyManager to do various private key operations using those
18
17
  # identities.
@@ -30,6 +29,12 @@ 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
+
35
+ # The list of user key certificate data that will be examined
36
+ attr_reader :keycert_data
37
+
33
38
  # The map of loaded identities
34
39
  attr_reader :known_identities
35
40
 
@@ -37,12 +42,15 @@ module Net
37
42
  attr_reader :options
38
43
 
39
44
  # Create a new KeyManager. By default, the manager will
40
- # use the ssh-agent (if it is running).
41
- def initialize(logger, options={})
45
+ # use the ssh-agent if it is running and the `:use_agent` option
46
+ # is not false.
47
+ def initialize(logger, options = {})
42
48
  self.logger = logger
43
49
  @key_files = []
44
50
  @key_data = []
45
- @use_agent = true
51
+ @keycert_files = []
52
+ @keycert_data = []
53
+ @use_agent = options[:use_agent] != false
46
54
  @known_identities = {}
47
55
  @agent = nil
48
56
  @options = options
@@ -55,6 +63,7 @@ module Net
55
63
  def clear!
56
64
  key_files.clear
57
65
  key_data.clear
66
+ keycert_data.clear
58
67
  known_identities.clear
59
68
  self
60
69
  end
@@ -65,6 +74,18 @@ module Net
65
74
  self
66
75
  end
67
76
 
77
+ # Add the given keycert_file to the list of keycert files that will be used.
78
+ def add_keycert(keycert_file)
79
+ keycert_files.push(File.expand_path(keycert_file)).uniq!
80
+ self
81
+ end
82
+
83
+ # Add the given keycert_data to the list of keycerts that will be used.
84
+ def add_keycert_data(keycert_data_)
85
+ keycert_data.push(keycert_data_).uniq!
86
+ self
87
+ end
88
+
68
89
  # Add the given key_file to the list of keys that will be used.
69
90
  def add_key_data(key_data_)
70
91
  key_data.push(key_data_).uniq!
@@ -97,7 +118,7 @@ module Net
97
118
  def each_identity
98
119
  prepared_identities = prepare_identities_from_files + prepare_identities_from_data
99
120
 
100
- user_identities = load_identities(prepared_identities, false)
121
+ user_identities = load_identities(prepared_identities, false, true)
101
122
 
102
123
  if agent
103
124
  agent.identities.each do |key|
@@ -107,13 +128,13 @@ module Net
107
128
  user_identities.delete(corresponding_user_identity) if corresponding_user_identity
108
129
 
109
130
  if !options[:keys_only] || corresponding_user_identity
110
- known_identities[key] = { :from => :agent }
131
+ known_identities[key] = { from: :agent, identity: key }
111
132
  yield key
112
133
  end
113
134
  end
114
135
  end
115
136
 
116
- user_identities = load_identities(user_identities, true)
137
+ user_identities = load_identities(user_identities, !options[:non_interactive], false)
117
138
 
118
139
  user_identities.each do |identity|
119
140
  key = identity.delete(:public_key)
@@ -121,6 +142,21 @@ module Net
121
142
  yield key
122
143
  end
123
144
 
145
+ known_identity_blobs = known_identities.keys.map(&:to_blob)
146
+
147
+ keycerts.each do |keycert|
148
+ next if known_identity_blobs.include?(keycert.to_blob)
149
+
150
+ (_, corresponding_identity) = known_identities.detect { |public_key, _|
151
+ public_key.to_pem == keycert.to_pem
152
+ }
153
+
154
+ if corresponding_identity
155
+ known_identities[keycert] = corresponding_identity
156
+ yield keycert
157
+ end
158
+ end
159
+
124
160
  self
125
161
  end
126
162
 
@@ -133,25 +169,39 @@ module Net
133
169
  # Regardless of the identity's origin or who does the signing, this
134
170
  # will always return the signature in an SSH2-specified "signature
135
171
  # blob" format.
136
- def sign(identity, data)
172
+ def sign(identity, data, sig_alg = nil)
137
173
  info = known_identities[identity] or raise KeyManagerError, "the given identity is unknown to the key manager"
138
174
 
139
175
  if info[:key].nil? && info[:from] == :file
140
176
  begin
141
- info[:key] = KeyFactory.load_private_key(info[:file], options[:passphrase], true)
142
- rescue Exception, OpenSSL::OpenSSLError => e
177
+ info[:key] = KeyFactory.load_private_key(info[:file], options[:passphrase], !options[:non_interactive], options[:password_prompt])
178
+ rescue OpenSSL::OpenSSLError, Exception => e
143
179
  raise KeyManagerError, "the given identity is known, but the private key could not be loaded: #{e.class} (#{e.message})"
144
180
  end
145
181
  end
146
182
 
147
183
  if info[:key]
148
- return Net::SSH::Buffer.from(:string, identity.ssh_type,
149
- :string, info[:key].ssh_do_sign(data.to_s)).to_s
184
+ if sig_alg.nil?
185
+ signed = info[:key].ssh_do_sign(data.to_s)
186
+ sig_alg = identity.ssh_signature_type
187
+ else
188
+ signed = info[:key].ssh_do_sign(data.to_s, sig_alg)
189
+ end
190
+ return Net::SSH::Buffer.from(:string, sig_alg,
191
+ :mstring, signed).to_s
150
192
  end
151
193
 
152
194
  if info[:from] == :agent
153
195
  raise KeyManagerError, "the agent is no longer available" unless agent
154
- return agent.sign(identity, data.to_s)
196
+
197
+ case sig_alg
198
+ when "rsa-sha2-512"
199
+ return agent.sign(info[:identity], data.to_s, Net::SSH::Authentication::Agent::SSH_AGENT_RSA_SHA2_512)
200
+ when "rsa-sha2-256"
201
+ return agent.sign(info[:identity], data.to_s, Net::SSH::Authentication::Agent::SSH_AGENT_RSA_SHA2_256)
202
+ else
203
+ return agent.sign(info[:identity], data.to_s)
204
+ end
155
205
  end
156
206
 
157
207
  raise KeyManagerError, "[BUG] can't determine identity origin (#{info.inspect})"
@@ -175,81 +225,102 @@ module Net
175
225
  # or if the agent is otherwise not available.
176
226
  def agent
177
227
  return unless use_agent?
178
- @agent ||= Agent.connect(logger)
228
+
229
+ @agent ||= Agent.connect(logger, options[:agent_socket_factory], options[:identity_agent])
179
230
  rescue AgentNotAvailable
180
231
  @use_agent = false
181
232
  nil
182
233
  end
183
234
 
235
+ def no_keys?
236
+ key_files.empty? && key_data.empty?
237
+ end
238
+
184
239
  private
185
240
 
241
+ # Load keycerts from files and data.
242
+ def keycerts
243
+ keycert_files.map { |keycert_file| KeyFactory.load_public_key(keycert_file) } +
244
+ keycert_data.map { |data| KeyFactory.load_data_public_key(data) }
245
+ end
246
+
186
247
  # Prepares identities from user key_files for loading, preserving their order and sources.
187
248
  def prepare_identities_from_files
188
249
  key_files.map do |file|
189
- public_key_file = file + ".pub"
190
- if File.readable?(public_key_file)
191
- { :load_from => :pubkey_file, :file => file }
192
- elsif File.readable?(file)
193
- { :load_from => :privkey_file, :file => file }
250
+ if readable_file?(file)
251
+ identity = {}
252
+ cert_file = file + "-cert.pub"
253
+ public_key_file = file + ".pub"
254
+ if readable_file?(cert_file)
255
+ identity[:load_from] = :pubkey_file
256
+ identity[:pubkey_file] = cert_file
257
+ elsif readable_file?(public_key_file)
258
+ identity[:load_from] = :pubkey_file
259
+ identity[:pubkey_file] = public_key_file
260
+ else
261
+ identity[:load_from] = :privkey_file
262
+ end
263
+ identity.merge(privkey_file: file)
194
264
  end
195
265
  end.compact
196
266
  end
197
267
 
268
+ def readable_file?(path)
269
+ File.file?(path) && File.readable?(path)
270
+ end
271
+
198
272
  # Prepared identities from user key_data, preserving their order and sources.
199
273
  def prepare_identities_from_data
200
274
  key_data.map do |data|
201
- { :load_from => :data, :data => data }
275
+ { load_from: :data, data: data }
202
276
  end
203
277
  end
204
278
 
205
- # Load prepared identities. Private key decryption errors ignored if passphrase was not prompted.
206
- def load_identities(identities, ask_passphrase)
279
+ # Load prepared identities. Private key decryption errors ignored if ignore_decryption_errors
280
+ def load_identities(identities, ask_passphrase, ignore_decryption_errors)
207
281
  identities.map do |identity|
208
- begin
209
- case identity[:load_from]
210
- when :pubkey_file
211
- key = KeyFactory.load_public_key(identity[:file] + ".pub")
212
- { :public_key => key, :from => :file, :file => identity[:file] }
213
- when :privkey_file
214
- private_key = KeyFactory.load_private_key(identity[:file], options[:passphrase], ask_passphrase)
215
- key = private_key.send(:public_key)
216
- { :public_key => key, :from => :file, :file => identity[:file], :key => private_key }
217
- when :data
218
- private_key = KeyFactory.load_data_private_key(identity[:data], options[:passphrase], ask_passphrase)
219
- key = private_key.send(:public_key)
220
- { :public_key => key, :from => :key_data, :data => identity[:data], :key => private_key }
221
- else
222
- identity
223
- end
224
-
225
- rescue OpenSSL::PKey::RSAError, OpenSSL::PKey::DSAError, OpenSSL::PKey::ECError => e
226
- if ask_passphrase
227
- process_identity_loading_error(identity, e)
228
- nil
229
- else
230
- identity
231
- end
232
- rescue ArgumentError => e
233
- process_identity_loading_error(identity, e)
234
- nil
235
- rescue Exception => e
282
+ case identity[:load_from]
283
+ when :pubkey_file
284
+ key = KeyFactory.load_public_key(identity[:pubkey_file])
285
+ { public_key: key, from: :file, file: identity[:privkey_file] }
286
+ when :privkey_file
287
+ private_key = KeyFactory.load_private_key(
288
+ identity[:privkey_file], options[:passphrase], ask_passphrase, options[:password_prompt]
289
+ )
290
+ key = private_key.send(:public_key)
291
+ { public_key: key, from: :file, file: identity[:privkey_file], key: private_key }
292
+ when :data
293
+ private_key = KeyFactory.load_data_private_key(
294
+ identity[:data], options[:passphrase], ask_passphrase, "<key in memory>", options[:password_prompt]
295
+ )
296
+ key = private_key.send(:public_key)
297
+ { public_key: key, from: :key_data, data: identity[:data], key: private_key }
298
+ else
299
+ identity
300
+ end
301
+ rescue OpenSSL::PKey::RSAError, OpenSSL::PKey::DSAError, OpenSSL::PKey::ECError, OpenSSL::PKey::PKeyError, ArgumentError => e
302
+ if ignore_decryption_errors
303
+ identity
304
+ else
236
305
  process_identity_loading_error(identity, e)
237
306
  nil
238
307
  end
308
+ rescue Exception => e
309
+ process_identity_loading_error(identity, e)
310
+ nil
239
311
  end.compact
240
312
  end
241
313
 
242
314
  def process_identity_loading_error(identity, e)
243
315
  case identity[:load_from]
244
316
  when :pubkey_file
245
- error { "could not load public key file `#{identity[:file]}': #{e.class} (#{e.message})" }
317
+ error { "could not load public key file `#{identity[:pubkey_file]}': #{e.class} (#{e.message})" }
246
318
  when :privkey_file
247
- error { "could not load private key file `#{identity[:file]}': #{e.class} (#{e.message})" }
319
+ error { "could not load private key file `#{identity[:privkey_file]}': #{e.class} (#{e.message})" }
248
320
  else
249
321
  raise e
250
322
  end
251
323
  end
252
-
253
324
  end
254
325
  end
255
326
  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