net-ssh 3.2.0 → 7.2.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/.dockerignore +6 -0
  4. data/.github/FUNDING.yml +1 -0
  5. data/.github/config/rubocop_linter_action.yml +4 -0
  6. data/.github/workflows/ci-with-docker.yml +44 -0
  7. data/.github/workflows/ci.yml +93 -0
  8. data/.github/workflows/rubocop.yml +16 -0
  9. data/.gitignore +13 -0
  10. data/.rubocop.yml +22 -0
  11. data/.rubocop_todo.yml +1081 -0
  12. data/CHANGES.txt +237 -7
  13. data/DEVELOPMENT.md +23 -0
  14. data/Dockerfile +27 -0
  15. data/Dockerfile.openssl3 +17 -0
  16. data/Gemfile +13 -0
  17. data/Gemfile.noed25519 +12 -0
  18. data/Gemfile.norbnacl +12 -0
  19. data/ISSUE_TEMPLATE.md +30 -0
  20. data/Manifest +4 -5
  21. data/README.md +298 -0
  22. data/Rakefile +125 -74
  23. data/SECURITY.md +4 -0
  24. data/appveyor.yml +58 -0
  25. data/docker-compose.yml +23 -0
  26. data/lib/net/ssh/authentication/agent.rb +279 -18
  27. data/lib/net/ssh/authentication/certificate.rb +183 -0
  28. data/lib/net/ssh/authentication/constants.rb +17 -15
  29. data/lib/net/ssh/authentication/ed25519.rb +186 -0
  30. data/lib/net/ssh/authentication/ed25519_loader.rb +31 -0
  31. data/lib/net/ssh/authentication/key_manager.rb +86 -39
  32. data/lib/net/ssh/authentication/methods/abstract.rb +67 -48
  33. data/lib/net/ssh/authentication/methods/hostbased.rb +34 -37
  34. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +13 -13
  35. data/lib/net/ssh/authentication/methods/none.rb +16 -19
  36. data/lib/net/ssh/authentication/methods/password.rb +27 -17
  37. data/lib/net/ssh/authentication/methods/publickey.rb +96 -55
  38. data/lib/net/ssh/authentication/pageant.rb +471 -367
  39. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +43 -0
  40. data/lib/net/ssh/authentication/session.rb +131 -121
  41. data/lib/net/ssh/buffer.rb +399 -300
  42. data/lib/net/ssh/buffered_io.rb +154 -150
  43. data/lib/net/ssh/config.rb +308 -185
  44. data/lib/net/ssh/connection/channel.rb +635 -613
  45. data/lib/net/ssh/connection/constants.rb +29 -29
  46. data/lib/net/ssh/connection/event_loop.rb +123 -0
  47. data/lib/net/ssh/connection/keepalive.rb +55 -51
  48. data/lib/net/ssh/connection/session.rb +620 -551
  49. data/lib/net/ssh/connection/term.rb +125 -123
  50. data/lib/net/ssh/errors.rb +101 -99
  51. data/lib/net/ssh/key_factory.rb +197 -105
  52. data/lib/net/ssh/known_hosts.rb +214 -127
  53. data/lib/net/ssh/loggable.rb +50 -49
  54. data/lib/net/ssh/packet.rb +83 -79
  55. data/lib/net/ssh/prompt.rb +50 -81
  56. data/lib/net/ssh/proxy/command.rb +105 -90
  57. data/lib/net/ssh/proxy/errors.rb +12 -10
  58. data/lib/net/ssh/proxy/http.rb +82 -79
  59. data/lib/net/ssh/proxy/https.rb +50 -0
  60. data/lib/net/ssh/proxy/jump.rb +54 -0
  61. data/lib/net/ssh/proxy/socks4.rb +2 -6
  62. data/lib/net/ssh/proxy/socks5.rb +14 -17
  63. data/lib/net/ssh/service/forward.rb +370 -317
  64. data/lib/net/ssh/test/channel.rb +145 -136
  65. data/lib/net/ssh/test/extensions.rb +131 -110
  66. data/lib/net/ssh/test/kex.rb +34 -32
  67. data/lib/net/ssh/test/local_packet.rb +46 -44
  68. data/lib/net/ssh/test/packet.rb +89 -70
  69. data/lib/net/ssh/test/remote_packet.rb +32 -30
  70. data/lib/net/ssh/test/script.rb +156 -142
  71. data/lib/net/ssh/test/socket.rb +49 -48
  72. data/lib/net/ssh/test.rb +82 -77
  73. data/lib/net/ssh/transport/algorithms.rb +462 -359
  74. data/lib/net/ssh/transport/chacha20_poly1305_cipher.rb +117 -0
  75. data/lib/net/ssh/transport/chacha20_poly1305_cipher_loader.rb +17 -0
  76. data/lib/net/ssh/transport/cipher_factory.rb +122 -99
  77. data/lib/net/ssh/transport/constants.rb +32 -24
  78. data/lib/net/ssh/transport/ctr.rb +42 -22
  79. data/lib/net/ssh/transport/hmac/abstract.rb +81 -63
  80. data/lib/net/ssh/transport/hmac/md5.rb +0 -2
  81. data/lib/net/ssh/transport/hmac/md5_96.rb +0 -2
  82. data/lib/net/ssh/transport/hmac/none.rb +0 -2
  83. data/lib/net/ssh/transport/hmac/ripemd160.rb +0 -2
  84. data/lib/net/ssh/transport/hmac/sha1.rb +0 -2
  85. data/lib/net/ssh/transport/hmac/sha1_96.rb +0 -2
  86. data/lib/net/ssh/transport/hmac/sha2_256.rb +7 -11
  87. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +4 -8
  88. data/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
  89. data/lib/net/ssh/transport/hmac/sha2_512.rb +6 -9
  90. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +4 -8
  91. data/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
  92. data/lib/net/ssh/transport/hmac.rb +14 -12
  93. data/lib/net/ssh/transport/identity_cipher.rb +54 -44
  94. data/lib/net/ssh/transport/kex/abstract.rb +130 -0
  95. data/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
  96. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +39 -0
  97. data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
  98. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +33 -40
  99. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
  100. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +119 -213
  101. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +53 -61
  102. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +5 -9
  103. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +36 -90
  104. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +18 -10
  105. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +18 -10
  106. data/lib/net/ssh/transport/kex.rb +15 -12
  107. data/lib/net/ssh/transport/key_expander.rb +24 -20
  108. data/lib/net/ssh/transport/openssl.rb +161 -124
  109. data/lib/net/ssh/transport/openssl_cipher_extensions.rb +8 -0
  110. data/lib/net/ssh/transport/packet_stream.rb +246 -185
  111. data/lib/net/ssh/transport/server_version.rb +55 -56
  112. data/lib/net/ssh/transport/session.rb +306 -255
  113. data/lib/net/ssh/transport/state.rb +178 -176
  114. data/lib/net/ssh/verifiers/accept_new.rb +33 -0
  115. data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +33 -0
  116. data/lib/net/ssh/verifiers/always.rb +58 -0
  117. data/lib/net/ssh/verifiers/never.rb +19 -0
  118. data/lib/net/ssh/version.rb +55 -53
  119. data/lib/net/ssh.rb +111 -47
  120. data/net-ssh-public_cert.pem +18 -18
  121. data/net-ssh.gemspec +38 -205
  122. data/support/ssh_tunnel_bug.rb +5 -5
  123. data.tar.gz.sig +0 -0
  124. metadata +173 -118
  125. metadata.gz.sig +0 -0
  126. data/.travis.yml +0 -18
  127. data/README.rdoc +0 -182
  128. data/lib/net/ssh/authentication/agent/java_pageant.rb +0 -85
  129. data/lib/net/ssh/authentication/agent/socket.rb +0 -178
  130. data/lib/net/ssh/ruby_compat.rb +0 -46
  131. data/lib/net/ssh/verifiers/lenient.rb +0 -30
  132. data/lib/net/ssh/verifiers/null.rb +0 -12
  133. data/lib/net/ssh/verifiers/secure.rb +0 -52
  134. data/lib/net/ssh/verifiers/strict.rb +0 -24
  135. data/setup.rb +0 -1585
  136. data/support/arcfour_check.rb +0 -20
  137. data/test/README.txt +0 -18
  138. data/test/authentication/methods/common.rb +0 -28
  139. data/test/authentication/methods/test_abstract.rb +0 -51
  140. data/test/authentication/methods/test_hostbased.rb +0 -114
  141. data/test/authentication/methods/test_keyboard_interactive.rb +0 -121
  142. data/test/authentication/methods/test_none.rb +0 -41
  143. data/test/authentication/methods/test_password.rb +0 -95
  144. data/test/authentication/methods/test_publickey.rb +0 -148
  145. data/test/authentication/test_agent.rb +0 -232
  146. data/test/authentication/test_key_manager.rb +0 -240
  147. data/test/authentication/test_session.rb +0 -107
  148. data/test/common.rb +0 -125
  149. data/test/configs/auth_off +0 -5
  150. data/test/configs/auth_on +0 -4
  151. data/test/configs/empty +0 -0
  152. data/test/configs/eqsign +0 -3
  153. data/test/configs/exact_match +0 -8
  154. data/test/configs/host_plus +0 -10
  155. data/test/configs/multihost +0 -4
  156. data/test/configs/negative_match +0 -6
  157. data/test/configs/nohost +0 -19
  158. data/test/configs/numeric_host +0 -4
  159. data/test/configs/proxy_remote_user +0 -2
  160. data/test/configs/send_env +0 -2
  161. data/test/configs/substitutes +0 -8
  162. data/test/configs/wild_cards +0 -14
  163. data/test/connection/test_channel.rb +0 -487
  164. data/test/connection/test_session.rb +0 -564
  165. data/test/integration/README.txt +0 -17
  166. data/test/integration/Vagrantfile +0 -12
  167. data/test/integration/common.rb +0 -63
  168. data/test/integration/playbook.yml +0 -56
  169. data/test/integration/test_forward.rb +0 -637
  170. data/test/integration/test_id_rsa_keys.rb +0 -96
  171. data/test/integration/test_proxy.rb +0 -93
  172. data/test/known_hosts/github +0 -1
  173. data/test/known_hosts/github_hash +0 -1
  174. data/test/manual/test_pageant.rb +0 -37
  175. data/test/start/test_connection.rb +0 -53
  176. data/test/start/test_options.rb +0 -57
  177. data/test/start/test_transport.rb +0 -28
  178. data/test/start/test_user_nil.rb +0 -27
  179. data/test/test_all.rb +0 -12
  180. data/test/test_buffer.rb +0 -433
  181. data/test/test_buffered_io.rb +0 -63
  182. data/test/test_config.rb +0 -268
  183. data/test/test_key_factory.rb +0 -191
  184. data/test/test_known_hosts.rb +0 -66
  185. data/test/transport/hmac/test_md5.rb +0 -41
  186. data/test/transport/hmac/test_md5_96.rb +0 -27
  187. data/test/transport/hmac/test_none.rb +0 -34
  188. data/test/transport/hmac/test_ripemd160.rb +0 -36
  189. data/test/transport/hmac/test_sha1.rb +0 -36
  190. data/test/transport/hmac/test_sha1_96.rb +0 -27
  191. data/test/transport/hmac/test_sha2_256.rb +0 -37
  192. data/test/transport/hmac/test_sha2_256_96.rb +0 -27
  193. data/test/transport/hmac/test_sha2_512.rb +0 -37
  194. data/test/transport/hmac/test_sha2_512_96.rb +0 -27
  195. data/test/transport/kex/test_diffie_hellman_group14_sha1.rb +0 -13
  196. data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +0 -150
  197. data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +0 -96
  198. data/test/transport/kex/test_diffie_hellman_group_exchange_sha256.rb +0 -19
  199. data/test/transport/kex/test_ecdh_sha2_nistp256.rb +0 -161
  200. data/test/transport/kex/test_ecdh_sha2_nistp384.rb +0 -38
  201. data/test/transport/kex/test_ecdh_sha2_nistp521.rb +0 -38
  202. data/test/transport/test_algorithms.rb +0 -328
  203. data/test/transport/test_cipher_factory.rb +0 -443
  204. data/test/transport/test_hmac.rb +0 -34
  205. data/test/transport/test_identity_cipher.rb +0 -40
  206. data/test/transport/test_packet_stream.rb +0 -1762
  207. data/test/transport/test_server_version.rb +0 -74
  208. data/test/transport/test_session.rb +0 -331
  209. data/test/transport/test_state.rb +0 -181
  210. data/test/verifiers/test_secure.rb +0 -40
@@ -1,89 +1,150 @@
1
1
  require 'strscan'
2
2
  require 'openssl'
3
3
  require 'base64'
4
+ require 'delegate'
4
5
  require 'net/ssh/buffer'
6
+ require 'net/ssh/authentication/ed25519_loader'
5
7
 
6
- module Net; module SSH
8
+ module Net
9
+ module SSH
10
+ module HostKeyEntries
11
+ # regular public key entry
12
+ class PubKey < Delegator
13
+ def initialize(key, comment: nil) # rubocop:disable Lint/MissingSuper
14
+ @key = key
15
+ @comment = comment
16
+ end
7
17
 
8
- # Represents the result of a search in known hosts
9
- # see search_for
10
- class HostKeys < Array
11
- attr_reader :host
18
+ def ssh_type
19
+ @key.ssh_type
20
+ end
12
21
 
13
- def initialize(host_keys, host, known_hosts, options = {})
14
- super(host_keys)
15
- @host = host
16
- @known_hosts = known_hosts
17
- @options = options
18
- end
22
+ def ssh_types
23
+ [ssh_type]
24
+ end
19
25
 
20
- def add_host_key(key)
21
- @known_hosts.add(@host, key, @options)
22
- push(key)
23
- end
24
- end
26
+ def to_blob
27
+ @key.to_blob
28
+ end
25
29
 
26
- # Searches an OpenSSH-style known-host file for a given host, and returns all
27
- # matching keys. This is used to implement host-key verification, as well as
28
- # to determine what key a user prefers to use for a given host.
29
- #
30
- # This is used internally by Net::SSH, and will never need to be used directly
31
- # by consumers of the library.
32
- class KnownHosts
30
+ def __getobj__
31
+ Kernel.warn("Calling Net::SSH::Buffer methods on HostKeyEntries PubKey is deprecated")
32
+ @key
33
+ end
33
34
 
34
- if defined?(OpenSSL::PKey::EC)
35
- SUPPORTED_TYPE = %w(ssh-rsa ssh-dss
36
- ecdsa-sha2-nistp256
37
- ecdsa-sha2-nistp384
38
- ecdsa-sha2-nistp521)
39
- else
40
- SUPPORTED_TYPE = %w(ssh-rsa ssh-dss)
35
+ def matches_key?(server_key)
36
+ @key.ssh_type == server_key.ssh_type && @key.to_blob == server_key.to_blob
37
+ end
38
+ end
39
+
40
+ # @cert-authority entry
41
+ class CertAuthority
42
+ def ssh_types
43
+ %w[
44
+ ecdsa-sha2-nistp256-cert-v01@openssh.com
45
+ ecdsa-sha2-nistp384-cert-v01@openssh.com
46
+ ecdsa-sha2-nistp521-cert-v01@openssh.com
47
+ ssh-ed25519-cert-v01@openssh.com
48
+ ssh-rsa-cert-v01@openssh.com
49
+ ssh-rsa-cert-v00@openssh.com
50
+ ]
51
+ end
52
+
53
+ def initialize(key, comment: nil)
54
+ @key = key
55
+ @comment = comment
56
+ end
57
+
58
+ def matches_key?(server_key)
59
+ if ssh_types.include?(server_key.ssh_type)
60
+ server_key.signature_valid? && (server_key.signature_key.to_blob == @key.to_blob)
61
+ else
62
+ false
63
+ end
64
+ end
65
+ end
41
66
  end
42
67
 
68
+ # Represents the result of a search in known hosts
69
+ # see search_for
70
+ class HostKeys
71
+ include Enumerable
72
+ attr_reader :host
43
73
 
44
- class <<self
74
+ def initialize(host_keys, host, known_hosts, options = {})
75
+ @host_keys = host_keys
76
+ @host = host
77
+ @known_hosts = known_hosts
78
+ @options = options
79
+ end
45
80
 
46
- # Searches all known host files (see KnownHosts.hostfiles) for all keys
47
- # of the given host. Returns an enumerable of keys found.
48
- def search_for(host, options={})
49
- HostKeys.new(search_in(hostfiles(options), host), host, self, options)
81
+ def add_host_key(key)
82
+ @known_hosts.add(@host, key, @options)
83
+ @host_keys.push(key)
50
84
  end
51
85
 
52
- # Search for all known keys for the given host, in every file given in
53
- # the +files+ array. Returns the list of keys.
54
- def search_in(files, host)
55
- files.map { |file| KnownHosts.new(file).keys_for(host) }.flatten
86
+ def each(&block)
87
+ @host_keys.each(&block)
56
88
  end
57
89
 
58
- # Looks in the given +options+ hash for the :user_known_hosts_file and
59
- # :global_known_hosts_file keys, and returns an array of all known
60
- # hosts files. If the :user_known_hosts_file key is not set, the
61
- # default is returned (~/.ssh/known_hosts and ~/.ssh/known_hosts2). If
62
- # :global_known_hosts_file is not set, the default is used
63
- # (/etc/ssh/ssh_known_hosts and /etc/ssh/ssh_known_hosts2).
64
- #
65
- # If you only want the user known host files, you can pass :user as
66
- # the second option.
67
- def hostfiles(options, which=:all)
68
- files = []
90
+ def empty?
91
+ @host_keys.empty?
92
+ end
93
+ end
94
+
95
+ # Searches an OpenSSH-style known-host file for a given host, and returns all
96
+ # matching keys. This is used to implement host-key verification, as well as
97
+ # to determine what key a user prefers to use for a given host.
98
+ #
99
+ # This is used internally by Net::SSH, and will never need to be used directly
100
+ # by consumers of the library.
101
+ class KnownHosts
102
+ SUPPORTED_TYPE = %w[ssh-rsa ssh-dss
103
+ ecdsa-sha2-nistp256
104
+ ecdsa-sha2-nistp384
105
+ ecdsa-sha2-nistp521]
69
106
 
70
- if which == :all || which == :user
71
- files += Array(options[:user_known_hosts_file] || %w(~/.ssh/known_hosts ~/.ssh/known_hosts2))
107
+ SUPPORTED_TYPE.push('ssh-ed25519') if Net::SSH::Authentication::ED25519Loader::LOADED
108
+
109
+ class << self
110
+ # Searches all known host files (see KnownHosts.hostfiles) for all keys
111
+ # of the given host. Returns an enumerable of keys found.
112
+ def search_for(host, options = {})
113
+ HostKeys.new(search_in(hostfiles(options), host, options), host, self, options)
72
114
  end
73
115
 
74
- if which == :all || which == :global
75
- files += Array(options[:global_known_hosts_file] || %w(/etc/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts2))
116
+ # Search for all known keys for the given host, in every file given in
117
+ # the +files+ array. Returns the list of keys.
118
+ def search_in(files, host, options = {})
119
+ files.flat_map { |file| KnownHosts.new(file).keys_for(host, options) }
76
120
  end
77
121
 
78
- return files
79
- end
122
+ # Looks in the given +options+ hash for the :user_known_hosts_file and
123
+ # :global_known_hosts_file keys, and returns an array of all known
124
+ # hosts files. If the :user_known_hosts_file key is not set, the
125
+ # default is returned (~/.ssh/known_hosts and ~/.ssh/known_hosts2). If
126
+ # :global_known_hosts_file is not set, the default is used
127
+ # (/etc/ssh/ssh_known_hosts and /etc/ssh/ssh_known_hosts2).
128
+ #
129
+ # If you only want the user known host files, you can pass :user as
130
+ # the second option.
131
+ def hostfiles(options, which = :all)
132
+ files = []
133
+
134
+ files += Array(options[:user_known_hosts_file] || %w[~/.ssh/known_hosts ~/.ssh/known_hosts2]) if which == :all || which == :user
135
+
136
+ if which == :all || which == :global
137
+ files += Array(options[:global_known_hosts_file] || %w[/etc/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts2])
138
+ end
80
139
 
81
- # Looks in all user known host files (see KnownHosts.hostfiles) and tries to
82
- # add an entry for the given host and key to the first file it is able
83
- # to.
84
- def add(host, key, options={})
85
- hostfiles(options, :user).each do |file|
86
- begin
140
+ return files
141
+ end
142
+
143
+ # Looks in all user known host files (see KnownHosts.hostfiles) and tries to
144
+ # add an entry for the given host and key to the first file it is able
145
+ # to.
146
+ def add(host, key, options = {})
147
+ hostfiles(options, :user).each do |file|
87
148
  KnownHosts.new(file).add(host, key)
88
149
  return
89
150
  rescue SystemCallError
@@ -91,88 +152,114 @@ module Net; module SSH
91
152
  end
92
153
  end
93
154
  end
94
- end
95
155
 
96
- # The host-key file name that this KnownHosts instance will use to search
97
- # for keys.
98
- attr_reader :source
156
+ # The host-key file name that this KnownHosts instance will use to search
157
+ # for keys.
158
+ attr_reader :source
99
159
 
100
- # Instantiate a new KnownHosts instance that will search the given known-hosts
101
- # file. The path is expanded file File.expand_path.
102
- def initialize(source)
103
- @source = File.expand_path(source)
104
- end
160
+ # Instantiate a new KnownHosts instance that will search the given known-hosts
161
+ # file. The path is expanded file File.expand_path.
162
+ def initialize(source)
163
+ @source = File.expand_path(source)
164
+ end
105
165
 
106
- # Returns an array of all keys that are known to be associatd with the
107
- # given host. The +host+ parameter is either the domain name or ip address
108
- # of the host, or both (comma-separated). Additionally, if a non-standard
109
- # port is being used, it may be specified by putting the host (or ip, or
110
- # both) in square brackets, and appending the port outside the brackets
111
- # after a colon. Possible formats for +host+, then, are;
112
- #
113
- # "net.ssh.test"
114
- # "1.2.3.4"
115
- # "net.ssh.test,1.2.3.4"
116
- # "[net.ssh.test]:5555"
117
- # "[1,2,3,4]:5555"
118
- # "[net.ssh.test]:5555,[1.2.3.4]:5555
119
- def keys_for(host)
120
- keys = []
121
- return keys unless File.readable?(source)
166
+ # Returns an array of all keys that are known to be associatd with the
167
+ # given host. The +host+ parameter is either the domain name or ip address
168
+ # of the host, or both (comma-separated). Additionally, if a non-standard
169
+ # port is being used, it may be specified by putting the host (or ip, or
170
+ # both) in square brackets, and appending the port outside the brackets
171
+ # after a colon. Possible formats for +host+, then, are;
172
+ #
173
+ # "net.ssh.test"
174
+ # "1.2.3.4"
175
+ # "net.ssh.test,1.2.3.4"
176
+ # "[net.ssh.test]:5555"
177
+ # "[1,2,3,4]:5555"
178
+ # "[net.ssh.test]:5555,[1.2.3.4]:5555
179
+ def keys_for(host, options = {})
180
+ keys = []
181
+ return keys unless File.readable?(source)
182
+
183
+ entries = host.split(/,/)
184
+ host_name = entries[0]
185
+ host_ip = entries[1]
122
186
 
123
- entries = host.split(/,/)
187
+ File.open(source) do |file|
188
+ file.each_line do |line|
189
+ if line.start_with?('@')
190
+ marker, hosts, type, key_content, comment = line.split(' ')
191
+ else
192
+ marker = nil
193
+ hosts, type, key_content, comment = line.split(' ')
194
+ end
124
195
 
125
- File.open(source) do |file|
126
- scanner = StringScanner.new("")
127
- file.each_line do |line|
128
- scanner.string = line
196
+ # Skip empty line or one that is commented
197
+ next if hosts.nil? || hosts.start_with?('#')
129
198
 
130
- scanner.skip(/\s*/)
131
- next if scanner.match?(/$|#/)
199
+ hostlist = hosts.split(',')
132
200
 
133
- hostlist = scanner.scan(/\S+/).split(/,/)
134
- found = entries.all? { |entry| hostlist.include?(entry) } ||
135
- known_host_hash?(hostlist, entries, scanner)
136
- next unless found
201
+ next unless SUPPORTED_TYPE.include?(type)
137
202
 
138
- scanner.skip(/\s*/)
139
- type = scanner.scan(/\S+/)
203
+ found = hostlist.any? { |pattern| match(host_name, pattern) } || known_host_hash?(hostlist, entries)
204
+ next unless found
140
205
 
141
- next unless SUPPORTED_TYPE.include?(type)
206
+ found = hostlist.include?(host_ip) if options[:check_host_ip] && entries.size > 1 && hostlist.size > 1
207
+ next unless found
142
208
 
143
- scanner.skip(/\s*/)
144
- blob = scanner.rest.unpack("m*").first
145
- keys << Net::SSH::Buffer.new(blob).read_key
209
+ blob = key_content.unpack("m*").first
210
+ raw_key = Net::SSH::Buffer.new(blob).read_key
211
+
212
+ keys <<
213
+ if marker == "@cert-authority"
214
+ HostKeyEntries::CertAuthority.new(raw_key, comment: comment)
215
+ else
216
+ HostKeyEntries::PubKey.new(raw_key, comment: comment)
217
+ end
218
+ end
146
219
  end
220
+
221
+ keys
147
222
  end
148
223
 
149
- keys
150
- end
224
+ def match(host, pattern)
225
+ if pattern.include?('*') || pattern.include?('?')
226
+ # see man 8 sshd for pattern details
227
+ pattern_regexp = pattern.split('*', -1).map do |x|
228
+ x.split('?', -1).map do |y|
229
+ Regexp.escape(y)
230
+ end.join('.')
231
+ end.join('.*')
151
232
 
152
- # Indicates whether one of the entries matches an hostname that has been
153
- # stored as a HMAC-SHA1 hash in the known hosts.
154
- def known_host_hash?(hostlist, entries, scanner)
155
- if hostlist.size == 1 && hostlist.first =~ /\A\|1(\|.+){2}\z/
156
- chunks = hostlist.first.split(/\|/)
157
- salt = Base64.decode64(chunks[2])
158
- digest = OpenSSL::Digest.new('sha1')
159
- entries.each do |entry|
160
- hmac = OpenSSL::HMAC.digest(digest, salt, entry)
161
- return true if Base64.encode64(hmac).chomp == chunks[3]
233
+ host =~ Regexp.new("\\A#{pattern_regexp}\\z")
234
+ else
235
+ host == pattern
162
236
  end
163
237
  end
164
- false
165
- end
166
238
 
167
- # Tries to append an entry to the current source file for the given host
168
- # and key. If it is unable to (because the file is not writable, for
169
- # instance), an exception will be raised.
170
- def add(host, key)
171
- File.open(source, "a") do |file|
172
- blob = [Net::SSH::Buffer.from(:key, key).to_s].pack("m*").gsub(/\s/, "")
173
- file.puts "#{host} #{key.ssh_type} #{blob}"
239
+ # Indicates whether one of the entries matches an hostname that has been
240
+ # stored as a HMAC-SHA1 hash in the known hosts.
241
+ def known_host_hash?(hostlist, entries)
242
+ if hostlist.size == 1 && hostlist.first =~ /\A\|1(\|.+){2}\z/
243
+ chunks = hostlist.first.split(/\|/)
244
+ salt = Base64.decode64(chunks[2])
245
+ digest = OpenSSL::Digest.new('sha1')
246
+ entries.each do |entry|
247
+ hmac = OpenSSL::HMAC.digest(digest, salt, entry)
248
+ return true if Base64.encode64(hmac).chomp == chunks[3]
249
+ end
250
+ end
251
+ false
174
252
  end
175
- end
176
253
 
254
+ # Tries to append an entry to the current source file for the given host
255
+ # and key. If it is unable to (because the file is not writable, for
256
+ # instance), an exception will be raised.
257
+ def add(host, key)
258
+ File.open(source, "a") do |file|
259
+ blob = [Net::SSH::Buffer.from(:key, key).to_s].pack("m*").gsub(/\s/, "")
260
+ file.puts "#{host} #{key.ssh_type} #{blob}"
261
+ end
262
+ end
263
+ end
177
264
  end
178
- end; end
265
+ 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