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
@@ -1,69 +1,149 @@
1
1
  require 'strscan'
2
+ require 'openssl'
3
+ require 'delegate'
2
4
  require 'net/ssh/buffer'
5
+ require 'net/ssh/authentication/ed25519_loader'
3
6
 
4
- module Net; module SSH
7
+ module Net
8
+ module SSH
9
+ module HostKeyEntries
10
+ # regular public key entry
11
+ class PubKey < Delegator
12
+ def initialize(key, comment: nil) # rubocop:disable Lint/MissingSuper
13
+ @key = key
14
+ @comment = comment
15
+ end
5
16
 
6
- # Searches an OpenSSH-style known-host file for a given host, and returns all
7
- # matching keys. This is used to implement host-key verification, as well as
8
- # to determine what key a user prefers to use for a given host.
9
- #
10
- # This is used internally by Net::SSH, and will never need to be used directly
11
- # by consumers of the library.
12
- class KnownHosts
17
+ def ssh_type
18
+ @key.ssh_type
19
+ end
13
20
 
14
- if defined?(OpenSSL::PKey::EC)
15
- SUPPORTED_TYPE = %w(ssh-rsa ssh-dss
16
- ecdsa-sha2-nistp256
17
- ecdsa-sha2-nistp384
18
- ecdsa-sha2-nistp521)
19
- else
20
- SUPPORTED_TYPE = %w(ssh-rsa ssh-dss)
21
+ def ssh_types
22
+ [ssh_type]
23
+ end
24
+
25
+ def to_blob
26
+ @key.to_blob
27
+ end
28
+
29
+ def __getobj__
30
+ Kernel.warn("Calling Net::SSH::Buffer methods on HostKeyEntries PubKey is deprecated")
31
+ @key
32
+ end
33
+
34
+ def matches_key?(server_key)
35
+ @key.ssh_type == server_key.ssh_type && @key.to_blob == server_key.to_blob
36
+ end
37
+ end
38
+
39
+ # @cert-authority entry
40
+ class CertAuthority
41
+ def ssh_types
42
+ %w[
43
+ ecdsa-sha2-nistp256-cert-v01@openssh.com
44
+ ecdsa-sha2-nistp384-cert-v01@openssh.com
45
+ ecdsa-sha2-nistp521-cert-v01@openssh.com
46
+ ssh-ed25519-cert-v01@openssh.com
47
+ ssh-rsa-cert-v01@openssh.com
48
+ ssh-rsa-cert-v00@openssh.com
49
+ ]
50
+ end
51
+
52
+ def initialize(key, comment: nil)
53
+ @key = key
54
+ @comment = comment
55
+ end
56
+
57
+ def matches_key?(server_key)
58
+ if ssh_types.include?(server_key.ssh_type)
59
+ server_key.signature_valid? && (server_key.signature_key.to_blob == @key.to_blob)
60
+ else
61
+ false
62
+ end
63
+ end
64
+ end
21
65
  end
22
66
 
67
+ # Represents the result of a search in known hosts
68
+ # see search_for
69
+ class HostKeys
70
+ include Enumerable
71
+ attr_reader :host
23
72
 
24
- class <<self
73
+ def initialize(host_keys, host, known_hosts, options = {})
74
+ @host_keys = host_keys
75
+ @host = host
76
+ @known_hosts = known_hosts
77
+ @options = options
78
+ end
25
79
 
26
- # Searches all known host files (see KnownHosts.hostfiles) for all keys
27
- # of the given host. Returns an array of keys found.
28
- def search_for(host, options={})
29
- search_in(hostfiles(options), host)
80
+ def add_host_key(key)
81
+ @known_hosts.add(@host, key, @options)
82
+ @host_keys.push(key)
30
83
  end
31
84
 
32
- # Search for all known keys for the given host, in every file given in
33
- # the +files+ array. Returns the list of keys.
34
- def search_in(files, host)
35
- files.map { |file| KnownHosts.new(file).keys_for(host) }.flatten
85
+ def each(&block)
86
+ @host_keys.each(&block)
36
87
  end
37
88
 
38
- # Looks in the given +options+ hash for the :user_known_hosts_file and
39
- # :global_known_hosts_file keys, and returns an array of all known
40
- # hosts files. If the :user_known_hosts_file key is not set, the
41
- # default is returned (~/.ssh/known_hosts and ~/.ssh/known_hosts2). If
42
- # :global_known_hosts_file is not set, the default is used
43
- # (/etc/ssh/known_hosts and /etc/ssh/known_hosts2).
44
- #
45
- # If you only want the user known host files, you can pass :user as
46
- # the second option.
47
- def hostfiles(options, which=:all)
48
- files = []
89
+ def empty?
90
+ @host_keys.empty?
91
+ end
92
+ end
93
+
94
+ # Searches an OpenSSH-style known-host file for a given host, and returns all
95
+ # matching keys. This is used to implement host-key verification, as well as
96
+ # to determine what key a user prefers to use for a given host.
97
+ #
98
+ # This is used internally by Net::SSH, and will never need to be used directly
99
+ # by consumers of the library.
100
+ class KnownHosts
101
+ SUPPORTED_TYPE = %w[ssh-rsa ssh-dss
102
+ ecdsa-sha2-nistp256
103
+ ecdsa-sha2-nistp384
104
+ ecdsa-sha2-nistp521]
105
+
106
+ SUPPORTED_TYPE.push('ssh-ed25519') if Net::SSH::Authentication::ED25519Loader::LOADED
49
107
 
50
- if which == :all || which == :user
51
- files += Array(options[:user_known_hosts_file] || %w(~/.ssh/known_hosts ~/.ssh/known_hosts2))
108
+ class << self
109
+ # Searches all known host files (see KnownHosts.hostfiles) for all keys
110
+ # of the given host. Returns an enumerable of keys found.
111
+ def search_for(host, options = {})
112
+ HostKeys.new(search_in(hostfiles(options), host, options), host, self, options)
52
113
  end
53
114
 
54
- if which == :all || which == :global
55
- files += Array(options[:global_known_hosts_file] || %w(/etc/ssh/known_hosts /etc/ssh/known_hosts2))
115
+ # Search for all known keys for the given host, in every file given in
116
+ # the +files+ array. Returns the list of keys.
117
+ def search_in(files, host, options = {})
118
+ files.flat_map { |file| KnownHosts.new(file).keys_for(host, options) }
56
119
  end
57
120
 
58
- return files
59
- end
121
+ # Looks in the given +options+ hash for the :user_known_hosts_file and
122
+ # :global_known_hosts_file keys, and returns an array of all known
123
+ # hosts files. If the :user_known_hosts_file key is not set, the
124
+ # default is returned (~/.ssh/known_hosts and ~/.ssh/known_hosts2). If
125
+ # :global_known_hosts_file is not set, the default is used
126
+ # (/etc/ssh/ssh_known_hosts and /etc/ssh/ssh_known_hosts2).
127
+ #
128
+ # If you only want the user known host files, you can pass :user as
129
+ # the second option.
130
+ def hostfiles(options, which = :all)
131
+ files = []
132
+
133
+ files += Array(options[:user_known_hosts_file] || %w[~/.ssh/known_hosts ~/.ssh/known_hosts2]) if which == :all || which == :user
134
+
135
+ if which == :all || which == :global
136
+ files += Array(options[:global_known_hosts_file] || %w[/etc/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts2])
137
+ end
138
+
139
+ return files
140
+ end
60
141
 
61
- # Looks in all user known host files (see KnownHosts.hostfiles) and tries to
62
- # add an entry for the given host and key to the first file it is able
63
- # to.
64
- def add(host, key, options={})
65
- hostfiles(options, :user).each do |file|
66
- begin
142
+ # Looks in all user known host files (see KnownHosts.hostfiles) and tries to
143
+ # add an entry for the given host and key to the first file it is able
144
+ # to.
145
+ def add(host, key, options = {})
146
+ hostfiles(options, :user).each do |file|
67
147
  KnownHosts.new(file).add(host, key)
68
148
  return
69
149
  rescue SystemCallError
@@ -71,71 +151,114 @@ module Net; module SSH
71
151
  end
72
152
  end
73
153
  end
74
- end
75
154
 
76
- # The host-key file name that this KnownHosts instance will use to search
77
- # for keys.
78
- attr_reader :source
155
+ # The host-key file name that this KnownHosts instance will use to search
156
+ # for keys.
157
+ attr_reader :source
79
158
 
80
- # Instantiate a new KnownHosts instance that will search the given known-hosts
81
- # file. The path is expanded file File.expand_path.
82
- def initialize(source)
83
- @source = File.expand_path(source)
84
- end
159
+ # Instantiate a new KnownHosts instance that will search the given known-hosts
160
+ # file. The path is expanded file File.expand_path.
161
+ def initialize(source)
162
+ @source = File.expand_path(source)
163
+ end
85
164
 
86
- # Returns an array of all keys that are known to be associatd with the
87
- # given host. The +host+ parameter is either the domain name or ip address
88
- # of the host, or both (comma-separated). Additionally, if a non-standard
89
- # port is being used, it may be specified by putting the host (or ip, or
90
- # both) in square brackets, and appending the port outside the brackets
91
- # after a colon. Possible formats for +host+, then, are;
92
- #
93
- # "net.ssh.test"
94
- # "1.2.3.4"
95
- # "net.ssh.test,1.2.3.4"
96
- # "[net.ssh.test]:5555"
97
- # "[1,2,3,4]:5555"
98
- # "[net.ssh.test]:5555,[1.2.3.4]:5555
99
- def keys_for(host)
100
- keys = []
101
- return keys unless File.readable?(source)
165
+ # Returns an array of all keys that are known to be associatd with the
166
+ # given host. The +host+ parameter is either the domain name or ip address
167
+ # of the host, or both (comma-separated). Additionally, if a non-standard
168
+ # port is being used, it may be specified by putting the host (or ip, or
169
+ # both) in square brackets, and appending the port outside the brackets
170
+ # after a colon. Possible formats for +host+, then, are;
171
+ #
172
+ # "net.ssh.test"
173
+ # "1.2.3.4"
174
+ # "net.ssh.test,1.2.3.4"
175
+ # "[net.ssh.test]:5555"
176
+ # "[1,2,3,4]:5555"
177
+ # "[net.ssh.test]:5555,[1.2.3.4]:5555
178
+ def keys_for(host, options = {})
179
+ keys = []
180
+ return keys unless File.readable?(source)
181
+
182
+ entries = host.split(/,/)
183
+ host_name = entries[0]
184
+ host_ip = entries[1]
102
185
 
103
- entries = host.split(/,/)
186
+ File.open(source) do |file|
187
+ file.each_line do |line|
188
+ if line.start_with?('@')
189
+ marker, hosts, type, key_content, comment = line.split(' ')
190
+ else
191
+ marker = nil
192
+ hosts, type, key_content, comment = line.split(' ')
193
+ end
104
194
 
105
- File.open(source) do |file|
106
- scanner = StringScanner.new("")
107
- file.each_line do |line|
108
- scanner.string = line
195
+ # Skip empty line or one that is commented
196
+ next if hosts.nil? || hosts.start_with?('#')
109
197
 
110
- scanner.skip(/\s*/)
111
- next if scanner.match?(/$|#/)
198
+ hostlist = hosts.split(',')
112
199
 
113
- hostlist = scanner.scan(/\S+/).split(/,/)
114
- next unless entries.all? { |entry| hostlist.include?(entry) }
200
+ next unless SUPPORTED_TYPE.include?(type)
115
201
 
116
- scanner.skip(/\s*/)
117
- type = scanner.scan(/\S+/)
202
+ found = hostlist.any? { |pattern| match(host_name, pattern) } || known_host_hash?(hostlist, entries)
203
+ next unless found
118
204
 
119
- next unless SUPPORTED_TYPE.include?(type)
205
+ found = hostlist.include?(host_ip) if options[:check_host_ip] && entries.size > 1 && hostlist.size > 1
206
+ next unless found
120
207
 
121
- scanner.skip(/\s*/)
122
- blob = scanner.rest.unpack("m*").first
123
- keys << Net::SSH::Buffer.new(blob).read_key
208
+ blob = key_content.unpack("m*").first
209
+ raw_key = Net::SSH::Buffer.new(blob).read_key
210
+
211
+ keys <<
212
+ if marker == "@cert-authority"
213
+ HostKeyEntries::CertAuthority.new(raw_key, comment: comment)
214
+ else
215
+ HostKeyEntries::PubKey.new(raw_key, comment: comment)
216
+ end
217
+ end
124
218
  end
219
+
220
+ keys
125
221
  end
126
222
 
127
- keys
128
- end
223
+ def match(host, pattern)
224
+ if pattern.include?('*') || pattern.include?('?')
225
+ # see man 8 sshd for pattern details
226
+ pattern_regexp = pattern.split('*', -1).map do |x|
227
+ x.split('?', -1).map do |y|
228
+ Regexp.escape(y)
229
+ end.join('.')
230
+ end.join('.*')
129
231
 
130
- # Tries to append an entry to the current source file for the given host
131
- # and key. If it is unable to (because the file is not writable, for
132
- # instance), an exception will be raised.
133
- def add(host, key)
134
- File.open(source, "a") do |file|
135
- blob = [Net::SSH::Buffer.from(:key, key).to_s].pack("m*").gsub(/\s/, "")
136
- file.puts "#{host} #{key.ssh_type} #{blob}"
232
+ host =~ Regexp.new("\\A#{pattern_regexp}\\z")
233
+ else
234
+ host == pattern
235
+ end
137
236
  end
138
- end
139
237
 
238
+ # Indicates whether one of the entries matches an hostname that has been
239
+ # stored as a HMAC-SHA1 hash in the known hosts.
240
+ def known_host_hash?(hostlist, entries)
241
+ if hostlist.size == 1 && hostlist.first =~ /\A\|1(\|.+){2}\z/
242
+ chunks = hostlist.first.split(/\|/)
243
+ salt = chunks[2].unpack1("m")
244
+ digest = OpenSSL::Digest.new('sha1')
245
+ entries.each do |entry|
246
+ hmac = OpenSSL::HMAC.digest(digest, salt, entry)
247
+ return true if [hmac].pack("m").chomp == chunks[3]
248
+ end
249
+ end
250
+ false
251
+ end
252
+
253
+ # Tries to append an entry to the current source file for the given host
254
+ # and key. If it is unable to (because the file is not writable, for
255
+ # instance), an exception will be raised.
256
+ def add(host, key)
257
+ File.open(source, "a") do |file|
258
+ blob = [Net::SSH::Buffer.from(:key, key).to_s].pack("m*").gsub(/\s/, "")
259
+ file.puts "#{host} #{key.ssh_type} #{blob}"
260
+ end
261
+ end
262
+ end
140
263
  end
141
- end; end
264
+ end
@@ -1,61 +1,62 @@
1
- module Net; module SSH
2
-
3
- # A simple module to make logging easier to deal with. It assumes that the
4
- # logger instance (if not nil) quacks like a Logger object (in Ruby's
5
- # standard library). Although used primarily internally by Net::SSH, it
6
- # can easily be used to add Net::SSH-like logging to your own programs.
7
- #
8
- # class MyClass
9
- # include Net::SSH::Loggable
10
- # end
11
- #
12
- # Net::SSH.start(...) do |ssh|
13
- # obj = MyClass.new
14
- # obj.logger = ssh.logger
15
- # ...
16
- # end
17
- module Loggable
18
- # The logger instance that will be used to log messages. If nil, nothing
19
- # will be logged.
20
- attr_accessor :logger
21
-
22
- # Displays the result of yielding if the log level is Logger::DEBUG or
23
- # greater.
24
- def debug
25
- logger.add(Logger::DEBUG, nil, facility) { yield } if logger && logger.debug?
26
- end
1
+ module Net
2
+ module SSH
3
+ # A simple module to make logging easier to deal with. It assumes that the
4
+ # logger instance (if not nil) quacks like a Logger object (in Ruby's
5
+ # standard library). Although used primarily internally by Net::SSH, it
6
+ # can easily be used to add Net::SSH-like logging to your own programs.
7
+ #
8
+ # class MyClass
9
+ # include Net::SSH::Loggable
10
+ # end
11
+ #
12
+ # Net::SSH.start(...) do |ssh|
13
+ # obj = MyClass.new
14
+ # obj.logger = ssh.logger
15
+ # ...
16
+ # end
17
+ module Loggable
18
+ # The logger instance that will be used to log messages. If nil, nothing
19
+ # will be logged.
20
+ attr_accessor :logger
21
+
22
+ # Displays the result of yielding if the log level is Logger::DEBUG or
23
+ # greater.
24
+ def debug
25
+ logger.add(Logger::DEBUG, nil, facility) { yield } if logger && logger.debug?
26
+ end
27
27
 
28
- # Displays the result of yielding if the log level is Logger::INFO or
29
- # greater.
30
- def info
31
- logger.add(Logger::INFO, nil, facility) { yield } if logger && logger.info?
32
- end
28
+ # Displays the result of yielding if the log level is Logger::INFO or
29
+ # greater.
30
+ def info
31
+ logger.add(Logger::INFO, nil, facility) { yield } if logger && logger.info?
32
+ end
33
33
 
34
- # Displays the result of yielding if the log level is Logger::WARN or
35
- # greater. (Called lwarn to avoid shadowing with Kernel#warn.)
36
- def lwarn
37
- logger.add(Logger::WARN, nil, facility) { yield } if logger && logger.warn?
38
- end
34
+ # Displays the result of yielding if the log level is Logger::WARN or
35
+ # greater. (Called lwarn to avoid shadowing with Kernel#warn.)
36
+ def lwarn
37
+ logger.add(Logger::WARN, nil, facility) { yield } if logger && logger.warn?
38
+ end
39
39
 
40
- # Displays the result of yielding if the log level is Logger:ERROR or
41
- # greater.
42
- def error
43
- logger.add(Logger::ERROR, nil, facility) { yield } if logger && logger.error?
44
- end
40
+ # Displays the result of yielding if the log level is Logger:ERROR or
41
+ # greater.
42
+ def error
43
+ logger.add(Logger::ERROR, nil, facility) { yield } if logger && logger.error?
44
+ end
45
45
 
46
- # Displays the result of yielding if the log level is Logger::FATAL or
47
- # greater.
48
- def fatal
49
- logger.add(Logger::FATAL, nil, facility) { yield } if logger && logger.fatal?
50
- end
46
+ # Displays the result of yielding if the log level is Logger::FATAL or
47
+ # greater.
48
+ def fatal
49
+ logger.add(Logger::FATAL, nil, facility) { yield } if logger && logger.fatal?
50
+ end
51
51
 
52
- private
52
+ private
53
53
 
54
54
  # Sets the "facility" value, used for reporting where a log message
55
55
  # originates. It defaults to the name of class with the object_id
56
56
  # appended.
57
57
  def facility
58
- @facility ||= self.class.name.gsub(/::/, ".").gsub(/([a-z])([A-Z])/, "\\1_\\2").downcase + "[%x]" % object_id
58
+ @facility ||= self.class.to_s.gsub(/::/, ".").gsub(/([a-z])([A-Z])/, "\\1_\\2").downcase + "[%x]" % object_id
59
59
  end
60
+ end
60
61
  end
61
- end; end
62
+ end
@@ -3,100 +3,104 @@ require 'net/ssh/transport/constants'
3
3
  require 'net/ssh/authentication/constants'
4
4
  require 'net/ssh/connection/constants'
5
5
 
6
- module Net; module SSH
7
-
8
- # A specialization of Buffer that knows the format of certain common
9
- # packet types. It auto-parses those packet types, and allows them to
10
- # be accessed via the #[] accessor.
11
- #
12
- # data = some_channel_request_packet
13
- # packet = Net::SSH::Packet.new(data)
14
- #
15
- # p packet.type #-> 98 (CHANNEL_REQUEST)
16
- # p packet[:request]
17
- # p packet[:want_reply]
18
- #
19
- # This is used exclusively internally by Net::SSH, and unless you're doing
20
- # protocol-level manipulation or are extending Net::SSH in some way, you'll
21
- # never need to use this class directly.
22
- class Packet < Buffer
23
- @@types = {}
24
-
25
- # Register a new packet type that should be recognized and auto-parsed by
26
- # Net::SSH::Packet. Note that any packet type that is not preregistered
27
- # will not be autoparsed.
6
+ module Net
7
+ module SSH
8
+ # A specialization of Buffer that knows the format of certain common
9
+ # packet types. It auto-parses those packet types, and allows them to
10
+ # be accessed via the #[] accessor.
28
11
  #
29
- # The +pairs+ parameter must be either empty, or an array of two-element
30
- # tuples, where the first element of each tuple is the name of the field,
31
- # and the second is the type.
12
+ # data = some_channel_request_packet
13
+ # packet = Net::SSH::Packet.new(data)
32
14
  #
33
- # register DISCONNECT, [:reason_code, :long], [:description, :string], [:language, :string]
34
- def self.register(type, *pairs)
35
- @@types[type] = pairs
36
- end
15
+ # p packet.type #-> 98 (CHANNEL_REQUEST)
16
+ # p packet[:request]
17
+ # p packet[:want_reply]
18
+ #
19
+ # This is used exclusively internally by Net::SSH, and unless you're doing
20
+ # protocol-level manipulation or are extending Net::SSH in some way, you'll
21
+ # never need to use this class directly.
22
+ class Packet < Buffer
23
+ @@types = {}
37
24
 
38
- include Transport::Constants, Authentication::Constants, Connection::Constants
25
+ # Register a new packet type that should be recognized and auto-parsed by
26
+ # Net::SSH::Packet. Note that any packet type that is not preregistered
27
+ # will not be autoparsed.
28
+ #
29
+ # The +pairs+ parameter must be either empty, or an array of two-element
30
+ # tuples, where the first element of each tuple is the name of the field,
31
+ # and the second is the type.
32
+ #
33
+ # register DISCONNECT, [:reason_code, :long], [:description, :string], [:language, :string]
34
+ def self.register(type, *pairs)
35
+ @@types[type] = pairs
36
+ end
39
37
 
40
- #--
41
- # These are the recognized packet types. All other packet types will be
42
- # accepted, but not auto-parsed, requiring the client to parse the
43
- # fields using the methods provided by Net::SSH::Buffer.
44
- #++
38
+ include Connection::Constants
39
+ include Authentication::Constants
40
+ include Transport::Constants
45
41
 
46
- register DISCONNECT, [:reason_code, :long], [:description, :string], [:language, :string]
47
- register IGNORE, [:data, :string]
48
- register UNIMPLEMENTED, [:number, :long]
49
- register DEBUG, [:always_display, :bool], [:message, :string], [:language, :string]
50
- register SERVICE_ACCEPT, [:service_name, :string]
51
- register USERAUTH_BANNER, [:message, :string], [:language, :string]
52
- register USERAUTH_FAILURE, [:authentications, :string], [:partial_success, :bool]
53
- register GLOBAL_REQUEST, [:request_type, :string], [:want_reply, :bool], [:request_data, :buffer]
54
- register CHANNEL_OPEN, [:channel_type, :string], [:remote_id, :long], [:window_size, :long], [:packet_size, :long]
55
- register CHANNEL_OPEN_CONFIRMATION, [:local_id, :long], [:remote_id, :long], [:window_size, :long], [:packet_size, :long]
56
- register CHANNEL_OPEN_FAILURE, [:local_id, :long], [:reason_code, :long], [:description, :string], [:language, :string]
57
- register CHANNEL_WINDOW_ADJUST, [:local_id, :long], [:extra_bytes, :long]
58
- register CHANNEL_DATA, [:local_id, :long], [:data, :string]
59
- register CHANNEL_EXTENDED_DATA, [:local_id, :long], [:data_type, :long], [:data, :string]
60
- register CHANNEL_EOF, [:local_id, :long]
61
- register CHANNEL_CLOSE, [:local_id, :long]
62
- register CHANNEL_REQUEST, [:local_id, :long], [:request, :string], [:want_reply, :bool], [:request_data, :buffer]
63
- register CHANNEL_SUCCESS, [:local_id, :long]
64
- register CHANNEL_FAILURE, [:local_id, :long]
42
+ #--
43
+ # These are the recognized packet types. All other packet types will be
44
+ # accepted, but not auto-parsed, requiring the client to parse the
45
+ # fields using the methods provided by Net::SSH::Buffer.
46
+ #++
65
47
 
66
- # The (integer) type of this packet.
67
- attr_reader :type
48
+ register DISCONNECT, %i[reason_code long], %i[description string], %i[language string]
49
+ register IGNORE, %i[data string]
50
+ register UNIMPLEMENTED, %i[number long]
51
+ register DEBUG, %i[always_display bool], %i[message string], %i[language string]
52
+ register SERVICE_ACCEPT, %i[service_name string]
53
+ register USERAUTH_BANNER, %i[message string], %i[language string]
54
+ register USERAUTH_FAILURE, %i[authentications string], %i[partial_success bool]
55
+ register GLOBAL_REQUEST, %i[request_type string], %i[want_reply bool], %i[request_data buffer]
56
+ register CHANNEL_OPEN, %i[channel_type string], %i[remote_id long], %i[window_size long], %i[packet_size long]
57
+ register CHANNEL_OPEN_CONFIRMATION, %i[local_id long], %i[remote_id long], %i[window_size long], %i[packet_size long]
58
+ register CHANNEL_OPEN_FAILURE, %i[local_id long], %i[reason_code long], %i[description string], %i[language string]
59
+ register CHANNEL_WINDOW_ADJUST, %i[local_id long], %i[extra_bytes long]
60
+ register CHANNEL_DATA, %i[local_id long], %i[data string]
61
+ register CHANNEL_EXTENDED_DATA, %i[local_id long], %i[data_type long], %i[data string]
62
+ register CHANNEL_EOF, %i[local_id long]
63
+ register CHANNEL_CLOSE, %i[local_id long]
64
+ register CHANNEL_REQUEST, %i[local_id long], %i[request string], %i[want_reply bool], %i[request_data buffer]
65
+ register CHANNEL_SUCCESS, %i[local_id long]
66
+ register CHANNEL_FAILURE, %i[local_id long]
68
67
 
69
- # Create a new packet from the given payload. This will automatically
70
- # parse the packet if it is one that has been previously registered with
71
- # Packet.register; otherwise, the packet will need to be manually parsed
72
- # using the methods provided in the Net::SSH::Buffer superclass.
73
- def initialize(payload)
74
- @named_elements = {}
75
- super
76
- @type = read_byte
77
- instantiate!
78
- end
68
+ # The (integer) type of this packet.
69
+ attr_reader :type
79
70
 
80
- # Access one of the auto-parsed fields by name. Raises an error if no
81
- # element by the given name exists.
82
- def [](name)
83
- name = name.to_sym
84
- raise ArgumentError, "no such element #{name}" unless @named_elements.key?(name)
85
- @named_elements[name]
86
- end
71
+ # Create a new packet from the given payload. This will automatically
72
+ # parse the packet if it is one that has been previously registered with
73
+ # Packet.register; otherwise, the packet will need to be manually parsed
74
+ # using the methods provided in the Net::SSH::Buffer superclass.
75
+ def initialize(payload)
76
+ @named_elements = {}
77
+ super
78
+ @type = read_byte
79
+ instantiate!
80
+ end
81
+
82
+ # Access one of the auto-parsed fields by name. Raises an error if no
83
+ # element by the given name exists.
84
+ def [](name)
85
+ name = name.to_sym
86
+ raise ArgumentError, "no such element #{name}" unless @named_elements.key?(name)
87
87
 
88
- private
88
+ @named_elements[name]
89
+ end
90
+
91
+ private
89
92
 
90
93
  # Parse the packet's contents and assign the named elements, as described
91
94
  # by the registered format for the packet.
92
95
  def instantiate!
93
96
  (@@types[type] || []).each do |name, datatype|
94
97
  @named_elements[name.to_sym] = if datatype == :buffer
95
- remainder_as_buffer
96
- else
97
- send("read_#{datatype}")
98
- end
98
+ remainder_as_buffer
99
+ else
100
+ send("read_#{datatype}")
101
+ end
99
102
  end
100
103
  end
104
+ end
101
105
  end
102
- end; end
106
+ end