minmb-net-ssh 2.5.1

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 (137) hide show
  1. data/CHANGELOG.rdoc +291 -0
  2. data/Manifest +132 -0
  3. data/README.rdoc +184 -0
  4. data/Rakefile +86 -0
  5. data/Rudyfile +96 -0
  6. data/THANKS.rdoc +19 -0
  7. data/lib/net/ssh.rb +223 -0
  8. data/lib/net/ssh/authentication/agent.rb +23 -0
  9. data/lib/net/ssh/authentication/agent/java_pageant.rb +85 -0
  10. data/lib/net/ssh/authentication/agent/socket.rb +170 -0
  11. data/lib/net/ssh/authentication/constants.rb +18 -0
  12. data/lib/net/ssh/authentication/key_manager.rb +253 -0
  13. data/lib/net/ssh/authentication/methods/abstract.rb +60 -0
  14. data/lib/net/ssh/authentication/methods/hostbased.rb +75 -0
  15. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +70 -0
  16. data/lib/net/ssh/authentication/methods/password.rb +43 -0
  17. data/lib/net/ssh/authentication/methods/publickey.rb +96 -0
  18. data/lib/net/ssh/authentication/pageant.rb +301 -0
  19. data/lib/net/ssh/authentication/session.rb +154 -0
  20. data/lib/net/ssh/buffer.rb +350 -0
  21. data/lib/net/ssh/buffered_io.rb +207 -0
  22. data/lib/net/ssh/config.rb +207 -0
  23. data/lib/net/ssh/connection/channel.rb +630 -0
  24. data/lib/net/ssh/connection/constants.rb +33 -0
  25. data/lib/net/ssh/connection/session.rb +603 -0
  26. data/lib/net/ssh/connection/term.rb +178 -0
  27. data/lib/net/ssh/errors.rb +88 -0
  28. data/lib/net/ssh/key_factory.rb +107 -0
  29. data/lib/net/ssh/known_hosts.rb +141 -0
  30. data/lib/net/ssh/loggable.rb +61 -0
  31. data/lib/net/ssh/packet.rb +102 -0
  32. data/lib/net/ssh/prompt.rb +93 -0
  33. data/lib/net/ssh/proxy/command.rb +75 -0
  34. data/lib/net/ssh/proxy/errors.rb +14 -0
  35. data/lib/net/ssh/proxy/http.rb +94 -0
  36. data/lib/net/ssh/proxy/socks4.rb +70 -0
  37. data/lib/net/ssh/proxy/socks5.rb +142 -0
  38. data/lib/net/ssh/ruby_compat.rb +77 -0
  39. data/lib/net/ssh/service/forward.rb +327 -0
  40. data/lib/net/ssh/test.rb +89 -0
  41. data/lib/net/ssh/test/channel.rb +129 -0
  42. data/lib/net/ssh/test/extensions.rb +152 -0
  43. data/lib/net/ssh/test/kex.rb +44 -0
  44. data/lib/net/ssh/test/local_packet.rb +51 -0
  45. data/lib/net/ssh/test/packet.rb +81 -0
  46. data/lib/net/ssh/test/remote_packet.rb +38 -0
  47. data/lib/net/ssh/test/script.rb +157 -0
  48. data/lib/net/ssh/test/socket.rb +64 -0
  49. data/lib/net/ssh/transport/algorithms.rb +407 -0
  50. data/lib/net/ssh/transport/cipher_factory.rb +106 -0
  51. data/lib/net/ssh/transport/constants.rb +32 -0
  52. data/lib/net/ssh/transport/ctr.rb +95 -0
  53. data/lib/net/ssh/transport/hmac.rb +45 -0
  54. data/lib/net/ssh/transport/hmac/abstract.rb +79 -0
  55. data/lib/net/ssh/transport/hmac/md5.rb +12 -0
  56. data/lib/net/ssh/transport/hmac/md5_96.rb +11 -0
  57. data/lib/net/ssh/transport/hmac/none.rb +15 -0
  58. data/lib/net/ssh/transport/hmac/ripemd160.rb +13 -0
  59. data/lib/net/ssh/transport/hmac/sha1.rb +13 -0
  60. data/lib/net/ssh/transport/hmac/sha1_96.rb +11 -0
  61. data/lib/net/ssh/transport/hmac/sha2_256.rb +15 -0
  62. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +13 -0
  63. data/lib/net/ssh/transport/hmac/sha2_512.rb +14 -0
  64. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +13 -0
  65. data/lib/net/ssh/transport/identity_cipher.rb +55 -0
  66. data/lib/net/ssh/transport/kex.rb +28 -0
  67. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +44 -0
  68. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +216 -0
  69. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +80 -0
  70. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +15 -0
  71. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +93 -0
  72. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +13 -0
  73. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +13 -0
  74. data/lib/net/ssh/transport/key_expander.rb +26 -0
  75. data/lib/net/ssh/transport/openssl.rb +237 -0
  76. data/lib/net/ssh/transport/packet_stream.rb +235 -0
  77. data/lib/net/ssh/transport/server_version.rb +71 -0
  78. data/lib/net/ssh/transport/session.rb +278 -0
  79. data/lib/net/ssh/transport/state.rb +206 -0
  80. data/lib/net/ssh/verifiers/lenient.rb +30 -0
  81. data/lib/net/ssh/verifiers/null.rb +12 -0
  82. data/lib/net/ssh/verifiers/strict.rb +53 -0
  83. data/lib/net/ssh/version.rb +62 -0
  84. data/net-ssh.gemspec +164 -0
  85. data/setup.rb +1585 -0
  86. data/support/arcfour_check.rb +20 -0
  87. data/support/ssh_tunnel_bug.rb +65 -0
  88. data/test/authentication/methods/common.rb +28 -0
  89. data/test/authentication/methods/test_abstract.rb +51 -0
  90. data/test/authentication/methods/test_hostbased.rb +114 -0
  91. data/test/authentication/methods/test_keyboard_interactive.rb +100 -0
  92. data/test/authentication/methods/test_password.rb +52 -0
  93. data/test/authentication/methods/test_publickey.rb +148 -0
  94. data/test/authentication/test_agent.rb +205 -0
  95. data/test/authentication/test_key_manager.rb +218 -0
  96. data/test/authentication/test_session.rb +106 -0
  97. data/test/common.rb +107 -0
  98. data/test/configs/eqsign +3 -0
  99. data/test/configs/exact_match +8 -0
  100. data/test/configs/host_plus +10 -0
  101. data/test/configs/multihost +4 -0
  102. data/test/configs/wild_cards +14 -0
  103. data/test/connection/test_channel.rb +467 -0
  104. data/test/connection/test_session.rb +488 -0
  105. data/test/known_hosts/github +1 -0
  106. data/test/test_all.rb +9 -0
  107. data/test/test_buffer.rb +426 -0
  108. data/test/test_buffered_io.rb +63 -0
  109. data/test/test_config.rb +120 -0
  110. data/test/test_key_factory.rb +121 -0
  111. data/test/test_known_hosts.rb +13 -0
  112. data/test/transport/hmac/test_md5.rb +39 -0
  113. data/test/transport/hmac/test_md5_96.rb +25 -0
  114. data/test/transport/hmac/test_none.rb +34 -0
  115. data/test/transport/hmac/test_ripemd160.rb +34 -0
  116. data/test/transport/hmac/test_sha1.rb +34 -0
  117. data/test/transport/hmac/test_sha1_96.rb +25 -0
  118. data/test/transport/hmac/test_sha2_256.rb +35 -0
  119. data/test/transport/hmac/test_sha2_256_96.rb +25 -0
  120. data/test/transport/hmac/test_sha2_512.rb +35 -0
  121. data/test/transport/hmac/test_sha2_512_96.rb +25 -0
  122. data/test/transport/kex/test_diffie_hellman_group14_sha1.rb +13 -0
  123. data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +146 -0
  124. data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +92 -0
  125. data/test/transport/kex/test_diffie_hellman_group_exchange_sha256.rb +33 -0
  126. data/test/transport/kex/test_ecdh_sha2_nistp256.rb +161 -0
  127. data/test/transport/kex/test_ecdh_sha2_nistp384.rb +37 -0
  128. data/test/transport/kex/test_ecdh_sha2_nistp521.rb +37 -0
  129. data/test/transport/test_algorithms.rb +330 -0
  130. data/test/transport/test_cipher_factory.rb +441 -0
  131. data/test/transport/test_hmac.rb +34 -0
  132. data/test/transport/test_identity_cipher.rb +40 -0
  133. data/test/transport/test_packet_stream.rb +1745 -0
  134. data/test/transport/test_server_version.rb +78 -0
  135. data/test/transport/test_session.rb +315 -0
  136. data/test/transport/test_state.rb +179 -0
  137. metadata +208 -0
@@ -0,0 +1,71 @@
1
+ require 'net/ssh/errors'
2
+ require 'net/ssh/loggable'
3
+ require 'net/ssh/version'
4
+
5
+ module Net; module SSH; module Transport
6
+
7
+ # Negotiates the SSH protocol version and trades information about server
8
+ # and client. This is never used directly--it is always called by the
9
+ # transport layer as part of the initialization process of the transport
10
+ # layer.
11
+ #
12
+ # Note that this class also encapsulates the negotiated version, and acts as
13
+ # the authoritative reference for any queries regarding the version in effect.
14
+ class ServerVersion
15
+ include Loggable
16
+
17
+ # The SSH version string as reported by Net::SSH
18
+ PROTO_VERSION = "SSH-2.0-Ruby/Net::SSH_#{Net::SSH::Version::CURRENT} #{RUBY_PLATFORM}"
19
+
20
+ # Any header text sent by the server prior to sending the version.
21
+ attr_reader :header
22
+
23
+ # The version string reported by the server.
24
+ attr_reader :version
25
+
26
+ # Instantiates a new ServerVersion and immediately (and synchronously)
27
+ # negotiates the SSH protocol in effect, using the given socket.
28
+ def initialize(socket, logger)
29
+ @header = ""
30
+ @version = nil
31
+ @logger = logger
32
+ negotiate!(socket)
33
+ end
34
+
35
+ private
36
+
37
+ # Negotiates the SSH protocol to use, via the given socket. If the server
38
+ # reports an incompatible SSH version (e.g., SSH1), this will raise an
39
+ # exception.
40
+ def negotiate!(socket)
41
+ info { "negotiating protocol version" }
42
+
43
+ loop do
44
+ @version = ""
45
+ loop do
46
+ begin
47
+ b = socket.readpartial(1)
48
+ raise Net::SSH::Disconnect, "connection closed by remote host" if b.nil?
49
+ rescue EOFError => e
50
+ raise Net::SSH::Disconnect, "connection closed by remote host"
51
+ end
52
+ @version << b
53
+ break if b == "\n"
54
+ end
55
+ break if @version.match(/^SSH-/)
56
+ @header << @version
57
+ end
58
+
59
+ @version.chomp!
60
+ debug { "remote is `#{@version}'" }
61
+
62
+ unless @version.match(/^SSH-(1\.99|2\.0)-/)
63
+ raise Net::SSH::Exception, "incompatible SSH version `#{@version}'"
64
+ end
65
+
66
+ debug { "local is `#{PROTO_VERSION}'" }
67
+ socket.write "#{PROTO_VERSION}\r\n"
68
+ socket.flush
69
+ end
70
+ end
71
+ end; end; end
@@ -0,0 +1,278 @@
1
+ require 'socket'
2
+ require 'timeout'
3
+
4
+ require 'net/ssh/errors'
5
+ require 'net/ssh/loggable'
6
+ require 'net/ssh/version'
7
+ require 'net/ssh/transport/algorithms'
8
+ require 'net/ssh/transport/constants'
9
+ require 'net/ssh/transport/packet_stream'
10
+ require 'net/ssh/transport/server_version'
11
+ require 'net/ssh/verifiers/null'
12
+ require 'net/ssh/verifiers/strict'
13
+ require 'net/ssh/verifiers/lenient'
14
+
15
+ module Net; module SSH; module Transport
16
+
17
+ # The transport layer represents the lowest level of the SSH protocol, and
18
+ # implements basic message exchanging and protocol initialization. It will
19
+ # never be instantiated directly (unless you really know what you're about),
20
+ # but will instead be created for you automatically when you create a new
21
+ # SSH session via Net::SSH.start.
22
+ class Session
23
+ include Constants, Loggable
24
+
25
+ # The standard port for the SSH protocol.
26
+ DEFAULT_PORT = 22
27
+
28
+ # The host to connect to, as given to the constructor.
29
+ attr_reader :host
30
+
31
+ # The port number to connect to, as given in the options to the constructor.
32
+ # If no port number was given, this will default to DEFAULT_PORT.
33
+ attr_reader :port
34
+
35
+ # The underlying socket object being used to communicate with the remote
36
+ # host.
37
+ attr_reader :socket
38
+
39
+ # The ServerVersion instance that encapsulates the negotiated protocol
40
+ # version.
41
+ attr_reader :server_version
42
+
43
+ # The Algorithms instance used to perform key exchanges.
44
+ attr_reader :algorithms
45
+
46
+ # The host-key verifier object used to verify host keys, to ensure that
47
+ # the connection is not being spoofed.
48
+ attr_reader :host_key_verifier
49
+
50
+ # The hash of options that were given to the object at initialization.
51
+ attr_reader :options
52
+
53
+ # Instantiates a new transport layer abstraction. This will block until
54
+ # the initial key exchange completes, leaving you with a ready-to-use
55
+ # transport session.
56
+ def initialize(host, options={})
57
+ self.logger = options[:logger]
58
+
59
+ @host = host
60
+ @port = options[:port] || DEFAULT_PORT
61
+ @bind_address = options[:bind_address] || nil
62
+ @options = options
63
+
64
+ debug { "establishing connection to #{@host}:#{@port}" }
65
+ factory = options[:proxy] || TCPSocket
66
+ @socket = timeout(options[:timeout] || 0) { @bind_address.nil? || options[:proxy] ? factory.open(@host, @port) : factory.open(@host,@port,@bind_address) }
67
+ @socket.extend(PacketStream)
68
+ @socket.logger = @logger
69
+
70
+ debug { "connection established" }
71
+
72
+ @queue = []
73
+
74
+ @host_key_verifier = select_host_key_verifier(options[:paranoid])
75
+
76
+
77
+ @server_version = timeout(options[:timeout] || 0) { ServerVersion.new(socket, logger) }
78
+
79
+ @algorithms = Algorithms.new(self, options)
80
+ wait { algorithms.initialized? }
81
+ end
82
+
83
+ # Returns the host (and possibly IP address) in a format compatible with
84
+ # SSH known-host files.
85
+ def host_as_string
86
+ @host_as_string ||= begin
87
+ string = "#{host}"
88
+ string = "[#{string}]:#{port}" if port != DEFAULT_PORT
89
+ if socket.peer_ip != host
90
+ string2 = socket.peer_ip
91
+ string2 = "[#{string2}]:#{port}" if port != DEFAULT_PORT
92
+ string << "," << string2
93
+ end
94
+ string
95
+ end
96
+ end
97
+
98
+ # Returns true if the underlying socket has been closed.
99
+ def closed?
100
+ socket.closed?
101
+ end
102
+
103
+ # Cleans up (see PacketStream#cleanup) and closes the underlying socket.
104
+ def close
105
+ socket.cleanup
106
+ socket.close
107
+ end
108
+
109
+ # Performs a "hard" shutdown of the connection. In general, this should
110
+ # never be done, but it might be necessary (in a rescue clause, for instance,
111
+ # when the connection needs to close but you don't know the status of the
112
+ # underlying protocol's state).
113
+ def shutdown!
114
+ error { "forcing connection closed" }
115
+ socket.close
116
+ end
117
+
118
+ # Returns a new service_request packet for the given service name, ready
119
+ # for sending to the server.
120
+ def service_request(service)
121
+ Net::SSH::Buffer.from(:byte, SERVICE_REQUEST, :string, service)
122
+ end
123
+
124
+ # Requests a rekey operation, and blocks until the operation completes.
125
+ # If a rekey is already pending, this returns immediately, having no
126
+ # effect.
127
+ def rekey!
128
+ if !algorithms.pending?
129
+ algorithms.rekey!
130
+ wait { algorithms.initialized? }
131
+ end
132
+ end
133
+
134
+ # Returns immediately if a rekey is already in process. Otherwise, if a
135
+ # rekey is needed (as indicated by the socket, see PacketStream#if_needs_rekey?)
136
+ # one is performed, causing this method to block until it completes.
137
+ def rekey_as_needed
138
+ return if algorithms.pending?
139
+ socket.if_needs_rekey? { rekey! }
140
+ end
141
+
142
+ # Returns a hash of information about the peer (remote) side of the socket,
143
+ # including :ip, :port, :host, and :canonized (see #host_as_string).
144
+ def peer
145
+ @peer ||= { :ip => socket.peer_ip, :port => @port.to_i, :host => @host, :canonized => host_as_string }
146
+ end
147
+
148
+ # Blocks until a new packet is available to be read, and returns that
149
+ # packet. See #poll_message.
150
+ def next_message
151
+ poll_message(:block)
152
+ end
153
+
154
+ # Tries to read the next packet from the socket. If mode is :nonblock (the
155
+ # default), this will not block and will return nil if there are no packets
156
+ # waiting to be read. Otherwise, this will block until a packet is
157
+ # available. Note that some packet types (DISCONNECT, IGNORE, UNIMPLEMENTED,
158
+ # DEBUG, and KEXINIT) are handled silently by this method, and will never
159
+ # be returned.
160
+ #
161
+ # If a key-exchange is in process and a disallowed packet type is
162
+ # received, it will be enqueued and otherwise ignored. When a key-exchange
163
+ # is not in process, and consume_queue is true, packets will be first
164
+ # read from the queue before the socket is queried.
165
+ def poll_message(mode=:nonblock, consume_queue=true)
166
+ loop do
167
+ if consume_queue && @queue.any? && algorithms.allow?(@queue.first)
168
+ return @queue.shift
169
+ end
170
+
171
+ packet = socket.next_packet(mode)
172
+ return nil if packet.nil?
173
+
174
+ case packet.type
175
+ when DISCONNECT
176
+ raise Net::SSH::Disconnect, "disconnected: #{packet[:description]} (#{packet[:reason_code]})"
177
+
178
+ when IGNORE
179
+ debug { "IGNORE packet recieved: #{packet[:data].inspect}" }
180
+
181
+ when UNIMPLEMENTED
182
+ lwarn { "UNIMPLEMENTED: #{packet[:number]}" }
183
+
184
+ when DEBUG
185
+ send(packet[:always_display] ? :fatal : :debug) { packet[:message] }
186
+
187
+ when KEXINIT
188
+ algorithms.accept_kexinit(packet)
189
+
190
+ else
191
+ return packet if algorithms.allow?(packet)
192
+ push(packet)
193
+ end
194
+ end
195
+ end
196
+
197
+ # Waits (blocks) until the given block returns true. If no block is given,
198
+ # this just waits long enough to see if there are any pending packets. Any
199
+ # packets read are enqueued (see #push).
200
+ def wait
201
+ loop do
202
+ break if block_given? && yield
203
+ message = poll_message(:nonblock, false)
204
+ push(message) if message
205
+ break if !block_given?
206
+ end
207
+ end
208
+
209
+ # Adds the given packet to the packet queue. If the queue is non-empty,
210
+ # #poll_message will return packets from the queue in the order they
211
+ # were received.
212
+ def push(packet)
213
+ @queue.push(packet)
214
+ end
215
+
216
+ # Sends the given message via the packet stream, blocking until the
217
+ # entire message has been sent.
218
+ def send_message(message)
219
+ socket.send_packet(message)
220
+ end
221
+
222
+ # Enqueues the given message, such that it will be sent at the earliest
223
+ # opportunity. This does not block, but returns immediately.
224
+ def enqueue_message(message)
225
+ socket.enqueue_packet(message)
226
+ end
227
+
228
+ # Configure's the packet stream's client state with the given set of
229
+ # options. This is typically used to define the cipher, compression, and
230
+ # hmac algorithms to use when sending packets to the server.
231
+ def configure_client(options={})
232
+ socket.client.set(options)
233
+ end
234
+
235
+ # Configure's the packet stream's server state with the given set of
236
+ # options. This is typically used to define the cipher, compression, and
237
+ # hmac algorithms to use when reading packets from the server.
238
+ def configure_server(options={})
239
+ socket.server.set(options)
240
+ end
241
+
242
+ # Sets a new hint for the packet stream, which the packet stream may use
243
+ # to change its behavior. (See PacketStream#hints).
244
+ def hint(which, value=true)
245
+ socket.hints[which] = value
246
+ end
247
+
248
+ public
249
+
250
+ # this method is primarily for use in tests
251
+ attr_reader :queue #:nodoc:
252
+
253
+ private
254
+
255
+ # Instantiates a new host-key verification class, based on the value of
256
+ # the parameter. When true or nil, the default Lenient verifier is
257
+ # returned. If it is false, the Null verifier is returned, and if it is
258
+ # :very, the Strict verifier is returned. If the argument happens to
259
+ # respond to :verify, it is returned directly. Otherwise, an exception
260
+ # is raised.
261
+ def select_host_key_verifier(paranoid)
262
+ case paranoid
263
+ when true, nil then
264
+ Net::SSH::Verifiers::Lenient.new
265
+ when false then
266
+ Net::SSH::Verifiers::Null.new
267
+ when :very then
268
+ Net::SSH::Verifiers::Strict.new
269
+ else
270
+ if paranoid.respond_to?(:verify)
271
+ paranoid
272
+ else
273
+ raise ArgumentError, "argument to :paranoid is not valid: #{paranoid.inspect}"
274
+ end
275
+ end
276
+ end
277
+ end
278
+ end; end; end
@@ -0,0 +1,206 @@
1
+ require 'zlib'
2
+ require 'net/ssh/transport/cipher_factory'
3
+ require 'net/ssh/transport/hmac'
4
+
5
+ module Net; module SSH; module Transport
6
+
7
+ # Encapsulates state information about one end of an SSH connection. Such
8
+ # state includes the packet sequence number, the algorithms in use, how
9
+ # many packets and blocks have been processed since the last reset, and so
10
+ # forth. This class will never be instantiated directly, but is used as
11
+ # part of the internal state of the PacketStream module.
12
+ class State
13
+ # The socket object that owns this state object.
14
+ attr_reader :socket
15
+
16
+ # The next packet sequence number for this socket endpoint.
17
+ attr_reader :sequence_number
18
+
19
+ # The hmac algorithm in use for this endpoint.
20
+ attr_reader :hmac
21
+
22
+ # The compression algorithm in use for this endpoint.
23
+ attr_reader :compression
24
+
25
+ # The compression level to use when compressing data (or nil, for the default).
26
+ attr_reader :compression_level
27
+
28
+ # The number of packets processed since the last call to #reset!
29
+ attr_reader :packets
30
+
31
+ # The number of data blocks processed since the last call to #reset!
32
+ attr_reader :blocks
33
+
34
+ # The cipher algorithm in use for this socket endpoint.
35
+ attr_reader :cipher
36
+
37
+ # The block size for the cipher
38
+ attr_reader :block_size
39
+
40
+ # The role that this state plays (either :client or :server)
41
+ attr_reader :role
42
+
43
+ # The maximum number of packets that this endpoint wants to process before
44
+ # needing a rekey.
45
+ attr_accessor :max_packets
46
+
47
+ # The maximum number of blocks that this endpoint wants to process before
48
+ # needing a rekey.
49
+ attr_accessor :max_blocks
50
+
51
+ # The user-specified maximum number of bytes that this endpoint ought to
52
+ # process before needing a rekey.
53
+ attr_accessor :rekey_limit
54
+
55
+ # Creates a new state object, belonging to the given socket. Initializes
56
+ # the algorithms to "none".
57
+ def initialize(socket, role)
58
+ @socket = socket
59
+ @role = role
60
+ @sequence_number = @packets = @blocks = 0
61
+ @cipher = CipherFactory.get("none")
62
+ @block_size = 8
63
+ @hmac = HMAC.get("none")
64
+ @compression = nil
65
+ @compressor = @decompressor = nil
66
+ @next_iv = ""
67
+ end
68
+
69
+ # A convenience method for quickly setting multiple values in a single
70
+ # command.
71
+ def set(values)
72
+ values.each do |key, value|
73
+ instance_variable_set("@#{key}", value)
74
+ end
75
+ reset!
76
+ end
77
+
78
+ def update_cipher(data)
79
+ result = cipher.update(data)
80
+ update_next_iv(role == :client ? result : data)
81
+ return result
82
+ end
83
+
84
+ def final_cipher
85
+ result = cipher.final
86
+ update_next_iv(role == :client ? result : "", true)
87
+ return result
88
+ end
89
+
90
+ # Increments the counters. The sequence number is incremented (and remapped
91
+ # so it always fits in a 32-bit integer). The number of packets and blocks
92
+ # are also incremented.
93
+ def increment(packet_length)
94
+ @sequence_number = (@sequence_number + 1) & 0xFFFFFFFF
95
+ @packets += 1
96
+ @blocks += (packet_length + 4) / @block_size
97
+ end
98
+
99
+ # The compressor object to use when compressing data. This takes into account
100
+ # the desired compression level.
101
+ def compressor
102
+ @compressor ||= Zlib::Deflate.new(compression_level || Zlib::DEFAULT_COMPRESSION)
103
+ end
104
+
105
+ # The decompressor object to use when decompressing data.
106
+ def decompressor
107
+ @decompressor ||= Zlib::Inflate.new(nil)
108
+ end
109
+
110
+ # Returns true if data compression/decompression is enabled. This will
111
+ # return true if :standard compression is selected, or if :delayed
112
+ # compression is selected and the :authenticated hint has been received
113
+ # by the socket.
114
+ def compression?
115
+ compression == :standard || (compression == :delayed && socket.hints[:authenticated])
116
+ end
117
+
118
+ # Compresses the data. If no compression is in effect, this will just return
119
+ # the data unmodified, otherwise it uses #compressor to compress the data.
120
+ def compress(data)
121
+ data = data.to_s
122
+ return data unless compression?
123
+ compressor.deflate(data, Zlib::SYNC_FLUSH)
124
+ end
125
+
126
+ # Deompresses the data. If no compression is in effect, this will just return
127
+ # the data unmodified, otherwise it uses #decompressor to decompress the data.
128
+ def decompress(data)
129
+ data = data.to_s
130
+ return data unless compression?
131
+ decompressor.inflate(data)
132
+ end
133
+
134
+ # Resets the counters on the state object, but leaves the sequence_number
135
+ # unchanged. It also sets defaults for and recomputes the max_packets and
136
+ # max_blocks values.
137
+ def reset!
138
+ @packets = @blocks = 0
139
+
140
+ @max_packets ||= 1 << 31
141
+
142
+ @block_size = cipher.name == "RC4" ? 8 : cipher.block_size
143
+
144
+ if max_blocks.nil?
145
+ # cargo-culted from openssh. the idea is that "the 2^(blocksize*2)
146
+ # limit is too expensive for 3DES, blowfish, etc., so enforce a 1GB
147
+ # limit for small blocksizes."
148
+ if @block_size >= 16
149
+ @max_blocks = 1 << (@block_size * 2)
150
+ else
151
+ @max_blocks = (1 << 30) / @block_size
152
+ end
153
+
154
+ # if a limit on the # of bytes has been given, convert that into a
155
+ # minimum number of blocks processed.
156
+
157
+ if rekey_limit
158
+ @max_blocks = [@max_blocks, rekey_limit / @block_size].min
159
+ end
160
+ end
161
+
162
+ cleanup
163
+ end
164
+
165
+ # Closes any the compressor and/or decompressor objects that have been
166
+ # instantiated.
167
+ def cleanup
168
+ if @compressor
169
+ @compressor.finish if !@compressor.finished?
170
+ @compressor.close
171
+ end
172
+
173
+ if @decompressor
174
+ # we call reset here so that we don't get warnings when we try to
175
+ # close the decompressor
176
+ @decompressor.reset
177
+ @decompressor.close
178
+ end
179
+
180
+ @compressor = @decompressor = nil
181
+ end
182
+
183
+ # Returns true if the number of packets processed exceeds the maximum
184
+ # number of packets, or if the number of blocks processed exceeds the
185
+ # maximum number of blocks.
186
+ def needs_rekey?
187
+ max_packets && packets > max_packets ||
188
+ max_blocks && blocks > max_blocks
189
+ end
190
+
191
+ private
192
+
193
+ def update_next_iv(data, reset=false)
194
+ @next_iv << data
195
+ @next_iv = @next_iv[-cipher.iv_len..-1]
196
+
197
+ if reset
198
+ cipher.reset
199
+ cipher.iv = @next_iv
200
+ end
201
+
202
+ return data
203
+ end
204
+ end
205
+
206
+ end; end; end