eximius-net-ssh 6.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +7 -0
  2. data/.dockerignore +6 -0
  3. data/.github/config/rubocop_linter_action.yml +4 -0
  4. data/.github/workflows/ci-with-docker.yml +44 -0
  5. data/.github/workflows/ci.yml +87 -0
  6. data/.github/workflows/rubocop.yml +13 -0
  7. data/.gitignore +13 -0
  8. data/.rubocop.yml +22 -0
  9. data/.rubocop_todo.yml +1072 -0
  10. data/CHANGES.txt +698 -0
  11. data/Dockerfile +27 -0
  12. data/Dockerfile.openssl3 +17 -0
  13. data/Gemfile +13 -0
  14. data/Gemfile.noed25519 +12 -0
  15. data/ISSUE_TEMPLATE.md +30 -0
  16. data/LICENSE.txt +19 -0
  17. data/Manifest +132 -0
  18. data/README.md +293 -0
  19. data/Rakefile +105 -0
  20. data/THANKS.txt +110 -0
  21. data/appveyor.yml +58 -0
  22. data/docker-compose.yml +23 -0
  23. data/lib/net/ssh/authentication/agent.rb +284 -0
  24. data/lib/net/ssh/authentication/certificate.rb +183 -0
  25. data/lib/net/ssh/authentication/constants.rb +20 -0
  26. data/lib/net/ssh/authentication/ed25519.rb +185 -0
  27. data/lib/net/ssh/authentication/ed25519_loader.rb +31 -0
  28. data/lib/net/ssh/authentication/key_manager.rb +310 -0
  29. data/lib/net/ssh/authentication/methods/abstract.rb +79 -0
  30. data/lib/net/ssh/authentication/methods/hostbased.rb +72 -0
  31. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +77 -0
  32. data/lib/net/ssh/authentication/methods/none.rb +34 -0
  33. data/lib/net/ssh/authentication/methods/password.rb +80 -0
  34. data/lib/net/ssh/authentication/methods/publickey.rb +137 -0
  35. data/lib/net/ssh/authentication/pageant.rb +497 -0
  36. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +43 -0
  37. data/lib/net/ssh/authentication/session.rb +165 -0
  38. data/lib/net/ssh/buffer.rb +440 -0
  39. data/lib/net/ssh/buffered_io.rb +202 -0
  40. data/lib/net/ssh/config.rb +406 -0
  41. data/lib/net/ssh/connection/channel.rb +695 -0
  42. data/lib/net/ssh/connection/constants.rb +33 -0
  43. data/lib/net/ssh/connection/event_loop.rb +123 -0
  44. data/lib/net/ssh/connection/keepalive.rb +59 -0
  45. data/lib/net/ssh/connection/session.rb +712 -0
  46. data/lib/net/ssh/connection/term.rb +180 -0
  47. data/lib/net/ssh/errors.rb +106 -0
  48. data/lib/net/ssh/key_factory.rb +218 -0
  49. data/lib/net/ssh/known_hosts.rb +265 -0
  50. data/lib/net/ssh/loggable.rb +62 -0
  51. data/lib/net/ssh/packet.rb +106 -0
  52. data/lib/net/ssh/prompt.rb +62 -0
  53. data/lib/net/ssh/proxy/command.rb +123 -0
  54. data/lib/net/ssh/proxy/errors.rb +16 -0
  55. data/lib/net/ssh/proxy/http.rb +98 -0
  56. data/lib/net/ssh/proxy/https.rb +50 -0
  57. data/lib/net/ssh/proxy/jump.rb +54 -0
  58. data/lib/net/ssh/proxy/socks4.rb +67 -0
  59. data/lib/net/ssh/proxy/socks5.rb +140 -0
  60. data/lib/net/ssh/service/forward.rb +426 -0
  61. data/lib/net/ssh/test/channel.rb +147 -0
  62. data/lib/net/ssh/test/extensions.rb +173 -0
  63. data/lib/net/ssh/test/kex.rb +46 -0
  64. data/lib/net/ssh/test/local_packet.rb +53 -0
  65. data/lib/net/ssh/test/packet.rb +101 -0
  66. data/lib/net/ssh/test/remote_packet.rb +40 -0
  67. data/lib/net/ssh/test/script.rb +180 -0
  68. data/lib/net/ssh/test/socket.rb +65 -0
  69. data/lib/net/ssh/test.rb +94 -0
  70. data/lib/net/ssh/transport/algorithms.rb +502 -0
  71. data/lib/net/ssh/transport/cipher_factory.rb +103 -0
  72. data/lib/net/ssh/transport/constants.rb +40 -0
  73. data/lib/net/ssh/transport/ctr.rb +115 -0
  74. data/lib/net/ssh/transport/hmac/abstract.rb +97 -0
  75. data/lib/net/ssh/transport/hmac/md5.rb +10 -0
  76. data/lib/net/ssh/transport/hmac/md5_96.rb +9 -0
  77. data/lib/net/ssh/transport/hmac/none.rb +13 -0
  78. data/lib/net/ssh/transport/hmac/ripemd160.rb +11 -0
  79. data/lib/net/ssh/transport/hmac/sha1.rb +11 -0
  80. data/lib/net/ssh/transport/hmac/sha1_96.rb +9 -0
  81. data/lib/net/ssh/transport/hmac/sha2_256.rb +11 -0
  82. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +9 -0
  83. data/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
  84. data/lib/net/ssh/transport/hmac/sha2_512.rb +11 -0
  85. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +9 -0
  86. data/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
  87. data/lib/net/ssh/transport/hmac.rb +47 -0
  88. data/lib/net/ssh/transport/identity_cipher.rb +57 -0
  89. data/lib/net/ssh/transport/kex/abstract.rb +130 -0
  90. data/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
  91. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +39 -0
  92. data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
  93. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +37 -0
  94. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
  95. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +122 -0
  96. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +72 -0
  97. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +11 -0
  98. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +39 -0
  99. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +21 -0
  100. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +21 -0
  101. data/lib/net/ssh/transport/kex.rb +31 -0
  102. data/lib/net/ssh/transport/key_expander.rb +30 -0
  103. data/lib/net/ssh/transport/openssl.rb +262 -0
  104. data/lib/net/ssh/transport/packet_stream.rb +280 -0
  105. data/lib/net/ssh/transport/server_version.rb +77 -0
  106. data/lib/net/ssh/transport/session.rb +354 -0
  107. data/lib/net/ssh/transport/state.rb +208 -0
  108. data/lib/net/ssh/verifiers/accept_new.rb +33 -0
  109. data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +33 -0
  110. data/lib/net/ssh/verifiers/always.rb +58 -0
  111. data/lib/net/ssh/verifiers/never.rb +19 -0
  112. data/lib/net/ssh/version.rb +70 -0
  113. data/lib/net/ssh.rb +330 -0
  114. data/net-ssh-public_cert.pem +20 -0
  115. data/net-ssh.gemspec +44 -0
  116. data/support/ssh_tunnel_bug.rb +65 -0
  117. metadata +277 -0
@@ -0,0 +1,354 @@
1
+ require 'socket'
2
+
3
+ require 'net/ssh/errors'
4
+ require 'net/ssh/loggable'
5
+ require 'net/ssh/version'
6
+ require 'net/ssh/transport/algorithms'
7
+ require 'net/ssh/transport/constants'
8
+ require 'net/ssh/transport/packet_stream'
9
+ require 'net/ssh/transport/server_version'
10
+ require 'net/ssh/verifiers/accept_new_or_local_tunnel'
11
+ require 'net/ssh/verifiers/accept_new'
12
+ require 'net/ssh/verifiers/always'
13
+ require 'net/ssh/verifiers/never'
14
+
15
+ module Net
16
+ module SSH
17
+ module Transport
18
+ # The transport layer represents the lowest level of the SSH protocol, and
19
+ # implements basic message exchanging and protocol initialization. It will
20
+ # never be instantiated directly (unless you really know what you're about),
21
+ # but will instead be created for you automatically when you create a new
22
+ # SSH session via Net::SSH.start.
23
+ class Session
24
+ include Loggable
25
+ include Constants
26
+
27
+ # The standard port for the SSH protocol.
28
+ DEFAULT_PORT = 22
29
+
30
+ # The host to connect to, as given to the constructor.
31
+ attr_reader :host
32
+
33
+ # The port number to connect to, as given in the options to the constructor.
34
+ # If no port number was given, this will default to DEFAULT_PORT.
35
+ attr_reader :port
36
+
37
+ # The underlying socket object being used to communicate with the remote
38
+ # host.
39
+ attr_reader :socket
40
+
41
+ # The ServerVersion instance that encapsulates the negotiated protocol
42
+ # version.
43
+ attr_reader :server_version
44
+
45
+ # The Algorithms instance used to perform key exchanges.
46
+ attr_reader :algorithms
47
+
48
+ # The host-key verifier object used to verify host keys, to ensure that
49
+ # the connection is not being spoofed.
50
+ attr_reader :host_key_verifier
51
+
52
+ # The hash of options that were given to the object at initialization.
53
+ attr_reader :options
54
+
55
+ # Instantiates a new transport layer abstraction. This will block until
56
+ # the initial key exchange completes, leaving you with a ready-to-use
57
+ # transport session.
58
+ def initialize(host, options = {})
59
+ self.logger = options[:logger]
60
+
61
+ @host = host
62
+ @port = options[:port] || DEFAULT_PORT
63
+ @bind_address = options[:bind_address] || nil
64
+ @options = options
65
+
66
+ @socket =
67
+ if (factory = options[:proxy])
68
+ debug { "establishing connection to #{@host}:#{@port} through proxy" }
69
+ factory.open(@host, @port, options)
70
+ else
71
+ debug { "establishing connection to #{@host}:#{@port}" }
72
+ Socket.tcp(@host, @port, @bind_address, nil,
73
+ connect_timeout: options[:timeout])
74
+ end
75
+
76
+ @socket.extend(PacketStream)
77
+ @socket.logger = @logger
78
+
79
+ debug { "connection established" }
80
+
81
+ @queue = []
82
+
83
+ @host_key_verifier = select_host_key_verifier(options[:verify_host_key])
84
+
85
+ @server_version = ServerVersion.new(socket, logger, options[:timeout])
86
+
87
+ @algorithms = Algorithms.new(self, options)
88
+ @algorithms.start
89
+ wait { algorithms.initialized? }
90
+ rescue Errno::ETIMEDOUT
91
+ raise Net::SSH::ConnectionTimeout
92
+ end
93
+
94
+ def host_keys
95
+ @host_keys ||= begin
96
+ known_hosts = options.fetch(:known_hosts, KnownHosts)
97
+ known_hosts.search_for(options[:host_key_alias] || host_as_string, options)
98
+ end
99
+ end
100
+
101
+ # Returns the host (and possibly IP address) in a format compatible with
102
+ # SSH known-host files.
103
+ def host_as_string
104
+ @host_as_string ||= begin
105
+ string = "#{host}"
106
+ string = "[#{string}]:#{port}" if port != DEFAULT_PORT
107
+
108
+ peer_ip = socket.peer_ip
109
+
110
+ if peer_ip != Net::SSH::Transport::PacketStream::PROXY_COMMAND_HOST_IP &&
111
+ peer_ip != host
112
+ string2 = peer_ip
113
+ string2 = "[#{string2}]:#{port}" if port != DEFAULT_PORT
114
+ string << "," << string2
115
+ end
116
+
117
+ string
118
+ end
119
+ end
120
+
121
+ # Returns true if the underlying socket has been closed.
122
+ def closed?
123
+ socket.closed?
124
+ end
125
+
126
+ # Cleans up (see PacketStream#cleanup) and closes the underlying socket.
127
+ def close
128
+ socket.cleanup
129
+ socket.close
130
+ end
131
+
132
+ # Performs a "hard" shutdown of the connection. In general, this should
133
+ # never be done, but it might be necessary (in a rescue clause, for instance,
134
+ # when the connection needs to close but you don't know the status of the
135
+ # underlying protocol's state).
136
+ def shutdown!
137
+ error { "forcing connection closed" }
138
+ socket.close
139
+ end
140
+
141
+ # Returns a new service_request packet for the given service name, ready
142
+ # for sending to the server.
143
+ def service_request(service)
144
+ Net::SSH::Buffer.from(:byte, SERVICE_REQUEST, :string, service)
145
+ end
146
+
147
+ # Requests a rekey operation, and blocks until the operation completes.
148
+ # If a rekey is already pending, this returns immediately, having no
149
+ # effect.
150
+ def rekey!
151
+ if !algorithms.pending?
152
+ algorithms.rekey!
153
+ wait { algorithms.initialized? }
154
+ end
155
+ end
156
+
157
+ # Returns immediately if a rekey is already in process. Otherwise, if a
158
+ # rekey is needed (as indicated by the socket, see PacketStream#if_needs_rekey?)
159
+ # one is performed, causing this method to block until it completes.
160
+ def rekey_as_needed
161
+ return if algorithms.pending?
162
+
163
+ socket.if_needs_rekey? { rekey! }
164
+ end
165
+
166
+ # Returns a hash of information about the peer (remote) side of the socket,
167
+ # including :ip, :port, :host, and :canonized (see #host_as_string).
168
+ def peer
169
+ @peer ||= { ip: socket.peer_ip, port: @port.to_i, host: @host, canonized: host_as_string }
170
+ end
171
+
172
+ # Blocks until a new packet is available to be read, and returns that
173
+ # packet. See #poll_message.
174
+ def next_message
175
+ poll_message(:block)
176
+ end
177
+
178
+ # Tries to read the next packet from the socket. If mode is :nonblock (the
179
+ # default), this will not block and will return nil if there are no packets
180
+ # waiting to be read. Otherwise, this will block until a packet is
181
+ # available. Note that some packet types (DISCONNECT, IGNORE, UNIMPLEMENTED,
182
+ # DEBUG, and KEXINIT) are handled silently by this method, and will never
183
+ # be returned.
184
+ #
185
+ # If a key-exchange is in process and a disallowed packet type is
186
+ # received, it will be enqueued and otherwise ignored. When a key-exchange
187
+ # is not in process, and consume_queue is true, packets will be first
188
+ # read from the queue before the socket is queried.
189
+ def poll_message(mode = :nonblock, consume_queue = true)
190
+ loop do
191
+ return @queue.shift if consume_queue && @queue.any? && algorithms.allow?(@queue.first)
192
+
193
+ packet = socket.next_packet(mode, options[:timeout])
194
+ return nil if packet.nil?
195
+
196
+ case packet.type
197
+ when DISCONNECT
198
+ raise Net::SSH::Disconnect, "disconnected: #{packet[:description]} (#{packet[:reason_code]})"
199
+
200
+ when IGNORE
201
+ debug { "IGNORE packet received: #{packet[:data].inspect}" }
202
+
203
+ when UNIMPLEMENTED
204
+ lwarn { "UNIMPLEMENTED: #{packet[:number]}" }
205
+
206
+ when DEBUG
207
+ send(packet[:always_display] ? :fatal : :debug) { packet[:message] }
208
+
209
+ when KEXINIT
210
+ algorithms.accept_kexinit(packet)
211
+
212
+ else
213
+ return packet if algorithms.allow?(packet)
214
+
215
+ push(packet)
216
+ end
217
+ end
218
+ end
219
+
220
+ # Waits (blocks) until the given block returns true. If no block is given,
221
+ # this just waits long enough to see if there are any pending packets. Any
222
+ # packets read are enqueued (see #push).
223
+ def wait
224
+ loop do
225
+ break if block_given? && yield
226
+
227
+ message = poll_message(:nonblock, false)
228
+ push(message) if message
229
+ break if !block_given?
230
+ end
231
+ end
232
+
233
+ # Adds the given packet to the packet queue. If the queue is non-empty,
234
+ # #poll_message will return packets from the queue in the order they
235
+ # were received.
236
+ def push(packet)
237
+ @queue.push(packet)
238
+ end
239
+
240
+ # Sends the given message via the packet stream, blocking until the
241
+ # entire message has been sent.
242
+ def send_message(message)
243
+ socket.send_packet(message)
244
+ end
245
+
246
+ # Enqueues the given message, such that it will be sent at the earliest
247
+ # opportunity. This does not block, but returns immediately.
248
+ def enqueue_message(message)
249
+ socket.enqueue_packet(message)
250
+ end
251
+
252
+ # Configure's the packet stream's client state with the given set of
253
+ # options. This is typically used to define the cipher, compression, and
254
+ # hmac algorithms to use when sending packets to the server.
255
+ def configure_client(options = {})
256
+ socket.client.set(options)
257
+ end
258
+
259
+ # Configure's the packet stream's server state with the given set of
260
+ # options. This is typically used to define the cipher, compression, and
261
+ # hmac algorithms to use when reading packets from the server.
262
+ def configure_server(options = {})
263
+ socket.server.set(options)
264
+ end
265
+
266
+ # Sets a new hint for the packet stream, which the packet stream may use
267
+ # to change its behavior. (See PacketStream#hints).
268
+ def hint(which, value = true)
269
+ socket.hints[which] = value
270
+ end
271
+
272
+ public
273
+
274
+ # this method is primarily for use in tests
275
+ attr_reader :queue # :nodoc:
276
+
277
+ private
278
+
279
+ # Compatibility verifier which allows users to keep using
280
+ # custom verifier code without adding new :verify_signature
281
+ # method.
282
+ class CompatibleVerifier
283
+ def initialize(verifier)
284
+ @verifier = verifier
285
+ end
286
+
287
+ def verify(arguments)
288
+ @verifier.verify(arguments)
289
+ end
290
+
291
+ def verify_signature(&block)
292
+ yield
293
+ end
294
+ end
295
+
296
+ # Instantiates a new host-key verification class, based on the value of
297
+ # the parameter.
298
+ #
299
+ # Usually, the argument is a symbol like `:never` which corresponds to
300
+ # a verifier, like `::Net::SSH::Verifiers::Never`.
301
+ #
302
+ # - :never (very insecure)
303
+ # - :accept_new_or_local_tunnel (insecure)
304
+ # - :accept_new (insecure)
305
+ # - :always (secure)
306
+ #
307
+ # If the argument happens to respond to :verify and :verify_signature,
308
+ # it is returned directly. Otherwise, an exception is raised.
309
+ #
310
+ # Values false, true, and :very were deprecated in
311
+ # [#595](https://github.com/net-ssh/net-ssh/pull/595)
312
+ def select_host_key_verifier(verifier)
313
+ case verifier
314
+ when false
315
+ Kernel.warn('verify_host_key: false is deprecated, use :never')
316
+ Net::SSH::Verifiers::Never.new
317
+ when :never then
318
+ Net::SSH::Verifiers::Never.new
319
+ when true
320
+ Kernel.warn('verify_host_key: true is deprecated, use :accept_new_or_local_tunnel')
321
+ Net::SSH::Verifiers::AcceptNewOrLocalTunnel.new
322
+ when :accept_new_or_local_tunnel, nil then
323
+ Net::SSH::Verifiers::AcceptNewOrLocalTunnel.new
324
+ when :very
325
+ Kernel.warn('verify_host_key: :very is deprecated, use :accept_new')
326
+ Net::SSH::Verifiers::AcceptNew.new
327
+ when :accept_new then
328
+ Net::SSH::Verifiers::AcceptNew.new
329
+ when :secure then
330
+ Kernel.warn('verify_host_key: :secure is deprecated, use :always')
331
+ Net::SSH::Verifiers::Always.new
332
+ when :always then
333
+ Net::SSH::Verifiers::Always.new
334
+ else
335
+ if verifier.respond_to?(:verify)
336
+ if verifier.respond_to?(:verify_signature)
337
+ verifier
338
+ else
339
+ Kernel.warn("Warning: verifier without :verify_signature is deprecated")
340
+ CompatibleVerifier.new(verifier)
341
+ end
342
+ else
343
+ raise(
344
+ ArgumentError,
345
+ "Invalid argument to :verify_host_key (or deprecated " \
346
+ ":paranoid): #{verifier.inspect}"
347
+ )
348
+ end
349
+ end
350
+ end
351
+ end
352
+ end
353
+ end
354
+ end
@@ -0,0 +1,208 @@
1
+ require 'zlib'
2
+ require 'net/ssh/transport/cipher_factory'
3
+ require 'net/ssh/transport/hmac'
4
+
5
+ module Net
6
+ module SSH
7
+ module Transport
8
+ # Encapsulates state information about one end of an SSH connection. Such
9
+ # state includes the packet sequence number, the algorithms in use, how
10
+ # many packets and blocks have been processed since the last reset, and so
11
+ # forth. This class will never be instantiated directly, but is used as
12
+ # part of the internal state of the PacketStream module.
13
+ class State
14
+ # The socket object that owns this state object.
15
+ attr_reader :socket
16
+
17
+ # The next packet sequence number for this socket endpoint.
18
+ attr_reader :sequence_number
19
+
20
+ # The hmac algorithm in use for this endpoint.
21
+ attr_reader :hmac
22
+
23
+ # The compression algorithm in use for this endpoint.
24
+ attr_reader :compression
25
+
26
+ # The compression level to use when compressing data (or nil, for the default).
27
+ attr_reader :compression_level
28
+
29
+ # The number of packets processed since the last call to #reset!
30
+ attr_reader :packets
31
+
32
+ # The number of data blocks processed since the last call to #reset!
33
+ attr_reader :blocks
34
+
35
+ # The cipher algorithm in use for this socket endpoint.
36
+ attr_reader :cipher
37
+
38
+ # The block size for the cipher
39
+ attr_reader :block_size
40
+
41
+ # The role that this state plays (either :client or :server)
42
+ attr_reader :role
43
+
44
+ # The maximum number of packets that this endpoint wants to process before
45
+ # needing a rekey.
46
+ attr_accessor :max_packets
47
+
48
+ # The maximum number of blocks that this endpoint wants to process before
49
+ # needing a rekey.
50
+ attr_accessor :max_blocks
51
+
52
+ # The user-specified maximum number of bytes that this endpoint ought to
53
+ # process before needing a rekey.
54
+ attr_accessor :rekey_limit
55
+
56
+ # Creates a new state object, belonging to the given socket. Initializes
57
+ # the algorithms to "none".
58
+ def initialize(socket, role)
59
+ @socket = socket
60
+ @role = role
61
+ @sequence_number = @packets = @blocks = 0
62
+ @cipher = CipherFactory.get("none")
63
+ @block_size = 8
64
+ @hmac = HMAC.get("none")
65
+ @compression = nil
66
+ @compressor = @decompressor = nil
67
+ @next_iv = String.new
68
+ end
69
+
70
+ # A convenience method for quickly setting multiple values in a single
71
+ # command.
72
+ def set(values)
73
+ values.each do |key, value|
74
+ instance_variable_set("@#{key}", value)
75
+ end
76
+ reset!
77
+ end
78
+
79
+ def update_cipher(data)
80
+ result = cipher.update(data)
81
+ update_next_iv(role == :client ? result : data)
82
+ return result
83
+ end
84
+
85
+ def final_cipher
86
+ result = cipher.final
87
+ update_next_iv(role == :client ? result : "", true)
88
+ return result
89
+ end
90
+
91
+ # Increments the counters. The sequence number is incremented (and remapped
92
+ # so it always fits in a 32-bit integer). The number of packets and blocks
93
+ # are also incremented.
94
+ def increment(packet_length)
95
+ @sequence_number = (@sequence_number + 1) & 0xFFFFFFFF
96
+ @packets += 1
97
+ @blocks += (packet_length + 4) / @block_size
98
+ end
99
+
100
+ # The compressor object to use when compressing data. This takes into account
101
+ # the desired compression level.
102
+ def compressor
103
+ @compressor ||= Zlib::Deflate.new(compression_level || Zlib::DEFAULT_COMPRESSION)
104
+ end
105
+
106
+ # The decompressor object to use when decompressing data.
107
+ def decompressor
108
+ @decompressor ||= Zlib::Inflate.new(nil)
109
+ end
110
+
111
+ # Returns true if data compression/decompression is enabled. This will
112
+ # return true if :standard compression is selected, or if :delayed
113
+ # compression is selected and the :authenticated hint has been received
114
+ # by the socket.
115
+ def compression?
116
+ compression == :standard || (compression == :delayed && socket.hints[:authenticated])
117
+ end
118
+
119
+ # Compresses the data. If no compression is in effect, this will just return
120
+ # the data unmodified, otherwise it uses #compressor to compress the data.
121
+ def compress(data)
122
+ data = data.to_s
123
+ return data unless compression?
124
+
125
+ compressor.deflate(data, Zlib::SYNC_FLUSH)
126
+ end
127
+
128
+ # Deompresses the data. If no compression is in effect, this will just return
129
+ # the data unmodified, otherwise it uses #decompressor to decompress the data.
130
+ def decompress(data)
131
+ data = data.to_s
132
+ return data unless compression?
133
+
134
+ decompressor.inflate(data)
135
+ end
136
+
137
+ # Resets the counters on the state object, but leaves the sequence_number
138
+ # unchanged. It also sets defaults for and recomputes the max_packets and
139
+ # max_blocks values.
140
+ def reset!
141
+ @packets = @blocks = 0
142
+
143
+ @max_packets ||= 1 << 31
144
+
145
+ @block_size = cipher.block_size
146
+
147
+ if max_blocks.nil?
148
+ # cargo-culted from openssh. the idea is that "the 2^(blocksize*2)
149
+ # limit is too expensive for 3DES, blowfish, etc., so enforce a 1GB
150
+ # limit for small blocksizes."
151
+ if @block_size >= 16
152
+ @max_blocks = 1 << (@block_size * 2)
153
+ else
154
+ @max_blocks = (1 << 30) / @block_size
155
+ end
156
+
157
+ # if a limit on the # of bytes has been given, convert that into a
158
+ # minimum number of blocks processed.
159
+
160
+ @max_blocks = [@max_blocks, rekey_limit / @block_size].min if rekey_limit
161
+ end
162
+
163
+ cleanup
164
+ end
165
+
166
+ # Closes any the compressor and/or decompressor objects that have been
167
+ # instantiated.
168
+ def cleanup
169
+ if @compressor
170
+ @compressor.finish if !@compressor.finished?
171
+ @compressor.close
172
+ end
173
+
174
+ if @decompressor
175
+ # we call reset here so that we don't get warnings when we try to
176
+ # close the decompressor
177
+ @decompressor.reset
178
+ @decompressor.close
179
+ end
180
+
181
+ @compressor = @decompressor = nil
182
+ end
183
+
184
+ # Returns true if the number of packets processed exceeds the maximum
185
+ # number of packets, or if the number of blocks processed exceeds the
186
+ # maximum number of blocks.
187
+ def needs_rekey?
188
+ max_packets && packets > max_packets ||
189
+ max_blocks && blocks > max_blocks
190
+ end
191
+
192
+ private
193
+
194
+ def update_next_iv(data, reset = false)
195
+ @next_iv << data
196
+ @next_iv = @next_iv[@next_iv.size - cipher.iv_len..-1]
197
+
198
+ if reset
199
+ cipher.reset
200
+ cipher.iv = @next_iv
201
+ end
202
+
203
+ return data
204
+ end
205
+ end
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,33 @@
1
+ require 'net/ssh/errors'
2
+ require 'net/ssh/known_hosts'
3
+ require 'net/ssh/verifiers/always'
4
+
5
+ module Net
6
+ module SSH
7
+ module Verifiers
8
+ # Does a strict host verification, looking the server up in the known
9
+ # host files to see if a key has already been seen for this server. If this
10
+ # server does not appear in any host file, this will silently add the
11
+ # server. If the server does appear at least once, but the key given does
12
+ # not match any known for the server, an exception will be raised (HostKeyMismatch).
13
+ # Otherwise, this returns true.
14
+ class AcceptNew < Always
15
+ def verify(arguments)
16
+ begin
17
+ super
18
+ rescue HostKeyUnknown => err
19
+ err.remember_host!
20
+ return true
21
+ end
22
+ end
23
+
24
+ def verify_signature(&block)
25
+ yield
26
+ rescue HostKeyUnknown => err
27
+ err.remember_host!
28
+ return true
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ require 'net/ssh/verifiers/accept_new'
2
+
3
+ module Net
4
+ module SSH
5
+ module Verifiers
6
+ # Basically the same as the AcceptNew verifier, but does not try to actually
7
+ # verify a connection if the server is the localhost and the port is a
8
+ # nonstandard port number. Those two conditions will typically mean the
9
+ # connection is being tunnelled through a forwarded port, so the known-hosts
10
+ # file will not be helpful (in general).
11
+ class AcceptNewOrLocalTunnel < AcceptNew
12
+ # Tries to determine if the connection is being tunnelled, and if so,
13
+ # returns true. Otherwise, performs the standard strict verification.
14
+ def verify(arguments)
15
+ return true if tunnelled?(arguments)
16
+
17
+ super
18
+ end
19
+
20
+ private
21
+
22
+ # A connection is potentially being tunnelled if the port is not 22,
23
+ # and the ip refers to the localhost.
24
+ def tunnelled?(args)
25
+ return false if args[:session].port == Net::SSH::Transport::Session::DEFAULT_PORT
26
+
27
+ ip = args[:session].peer[:ip]
28
+ return ip == "127.0.0.1" || ip == "::1"
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,58 @@
1
+ require 'net/ssh/errors'
2
+ require 'net/ssh/known_hosts'
3
+
4
+ module Net
5
+ module SSH
6
+ module Verifiers
7
+ # Does a strict host verification, looking the server up in the known
8
+ # host files to see if a key has already been seen for this server. If this
9
+ # server does not appear in any host file, an exception will be raised
10
+ # (HostKeyUnknown). This is in contrast to the "Strict" class, which will
11
+ # silently add the key to your known_hosts file. If the server does appear at
12
+ # least once, but the key given does not match any known for the server, an
13
+ # exception will be raised (HostKeyMismatch).
14
+ # Otherwise, this returns true.
15
+ class Always
16
+ def verify(arguments)
17
+ host_keys = arguments[:session].host_keys
18
+
19
+ # We've never seen this host before, so raise an exception.
20
+ process_cache_miss(host_keys, arguments, HostKeyUnknown, "is unknown") if host_keys.empty?
21
+
22
+ # If we found any matches, check to see that the key type and
23
+ # blob also match.
24
+
25
+ found = host_keys.any? do |key|
26
+ if key.respond_to?(:matches_key?)
27
+ key.matches_key?(arguments[:key])
28
+ else
29
+ key.ssh_type == arguments[:key].ssh_type && key.to_blob == arguments[:key].to_blob
30
+ end
31
+ end
32
+
33
+ # If a match was found, return true. Otherwise, raise an exception
34
+ # indicating that the key was not recognized.
35
+ process_cache_miss(host_keys, arguments, HostKeyMismatch, "does not match") unless found
36
+
37
+ found
38
+ end
39
+
40
+ def verify_signature(&block)
41
+ yield
42
+ end
43
+
44
+ private
45
+
46
+ def process_cache_miss(host_keys, args, exc_class, message)
47
+ exception = exc_class.new("fingerprint #{args[:fingerprint]} " +
48
+ "#{message} for #{host_keys.host.inspect}")
49
+ exception.data = args
50
+ exception.callback = Proc.new do
51
+ host_keys.add_host_key(args[:key])
52
+ end
53
+ raise exception
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end