ddollar-net-ssh 2.0.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 (103) hide show
  1. data/CHANGELOG.rdoc +42 -0
  2. data/Manifest +101 -0
  3. data/README.rdoc +110 -0
  4. data/Rakefile +26 -0
  5. data/THANKS.rdoc +16 -0
  6. data/lib/net/ssh.rb +199 -0
  7. data/lib/net/ssh/authentication/agent.rb +175 -0
  8. data/lib/net/ssh/authentication/constants.rb +18 -0
  9. data/lib/net/ssh/authentication/key_manager.rb +169 -0
  10. data/lib/net/ssh/authentication/methods/abstract.rb +60 -0
  11. data/lib/net/ssh/authentication/methods/hostbased.rb +71 -0
  12. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +66 -0
  13. data/lib/net/ssh/authentication/methods/password.rb +39 -0
  14. data/lib/net/ssh/authentication/methods/publickey.rb +92 -0
  15. data/lib/net/ssh/authentication/pageant.rb +176 -0
  16. data/lib/net/ssh/authentication/session.rb +127 -0
  17. data/lib/net/ssh/buffer.rb +339 -0
  18. data/lib/net/ssh/buffered_io.rb +149 -0
  19. data/lib/net/ssh/config.rb +173 -0
  20. data/lib/net/ssh/connection/channel.rb +625 -0
  21. data/lib/net/ssh/connection/constants.rb +33 -0
  22. data/lib/net/ssh/connection/session.rb +569 -0
  23. data/lib/net/ssh/connection/term.rb +178 -0
  24. data/lib/net/ssh/errors.rb +85 -0
  25. data/lib/net/ssh/key_factory.rb +85 -0
  26. data/lib/net/ssh/known_hosts.rb +129 -0
  27. data/lib/net/ssh/loggable.rb +61 -0
  28. data/lib/net/ssh/packet.rb +102 -0
  29. data/lib/net/ssh/prompt.rb +93 -0
  30. data/lib/net/ssh/proxy/errors.rb +14 -0
  31. data/lib/net/ssh/proxy/http.rb +94 -0
  32. data/lib/net/ssh/proxy/socks4.rb +70 -0
  33. data/lib/net/ssh/proxy/socks5.rb +128 -0
  34. data/lib/net/ssh/service/forward.rb +267 -0
  35. data/lib/net/ssh/test.rb +89 -0
  36. data/lib/net/ssh/test/channel.rb +129 -0
  37. data/lib/net/ssh/test/extensions.rb +152 -0
  38. data/lib/net/ssh/test/kex.rb +44 -0
  39. data/lib/net/ssh/test/local_packet.rb +51 -0
  40. data/lib/net/ssh/test/packet.rb +81 -0
  41. data/lib/net/ssh/test/remote_packet.rb +38 -0
  42. data/lib/net/ssh/test/script.rb +157 -0
  43. data/lib/net/ssh/test/socket.rb +59 -0
  44. data/lib/net/ssh/transport/algorithms.rb +384 -0
  45. data/lib/net/ssh/transport/cipher_factory.rb +72 -0
  46. data/lib/net/ssh/transport/constants.rb +30 -0
  47. data/lib/net/ssh/transport/hmac.rb +31 -0
  48. data/lib/net/ssh/transport/hmac/abstract.rb +48 -0
  49. data/lib/net/ssh/transport/hmac/md5.rb +12 -0
  50. data/lib/net/ssh/transport/hmac/md5_96.rb +11 -0
  51. data/lib/net/ssh/transport/hmac/none.rb +15 -0
  52. data/lib/net/ssh/transport/hmac/sha1.rb +13 -0
  53. data/lib/net/ssh/transport/hmac/sha1_96.rb +11 -0
  54. data/lib/net/ssh/transport/identity_cipher.rb +40 -0
  55. data/lib/net/ssh/transport/kex.rb +13 -0
  56. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +208 -0
  57. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +77 -0
  58. data/lib/net/ssh/transport/openssl.rb +128 -0
  59. data/lib/net/ssh/transport/packet_stream.rb +230 -0
  60. data/lib/net/ssh/transport/server_version.rb +61 -0
  61. data/lib/net/ssh/transport/session.rb +262 -0
  62. data/lib/net/ssh/transport/state.rb +170 -0
  63. data/lib/net/ssh/verifiers/lenient.rb +30 -0
  64. data/lib/net/ssh/verifiers/null.rb +12 -0
  65. data/lib/net/ssh/verifiers/strict.rb +53 -0
  66. data/lib/net/ssh/version.rb +60 -0
  67. data/net-ssh.gemspec +56 -0
  68. data/setup.rb +1585 -0
  69. data/test/authentication/methods/common.rb +28 -0
  70. data/test/authentication/methods/test_abstract.rb +51 -0
  71. data/test/authentication/methods/test_hostbased.rb +108 -0
  72. data/test/authentication/methods/test_keyboard_interactive.rb +98 -0
  73. data/test/authentication/methods/test_password.rb +50 -0
  74. data/test/authentication/methods/test_publickey.rb +123 -0
  75. data/test/authentication/test_agent.rb +205 -0
  76. data/test/authentication/test_key_manager.rb +100 -0
  77. data/test/authentication/test_session.rb +93 -0
  78. data/test/common.rb +106 -0
  79. data/test/configs/exact_match +8 -0
  80. data/test/configs/wild_cards +14 -0
  81. data/test/connection/test_channel.rb +452 -0
  82. data/test/connection/test_session.rb +483 -0
  83. data/test/test_all.rb +6 -0
  84. data/test/test_buffer.rb +336 -0
  85. data/test/test_buffered_io.rb +63 -0
  86. data/test/test_config.rb +78 -0
  87. data/test/test_key_factory.rb +67 -0
  88. data/test/transport/hmac/test_md5.rb +34 -0
  89. data/test/transport/hmac/test_md5_96.rb +25 -0
  90. data/test/transport/hmac/test_none.rb +34 -0
  91. data/test/transport/hmac/test_sha1.rb +34 -0
  92. data/test/transport/hmac/test_sha1_96.rb +25 -0
  93. data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +146 -0
  94. data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +92 -0
  95. data/test/transport/test_algorithms.rb +302 -0
  96. data/test/transport/test_cipher_factory.rb +163 -0
  97. data/test/transport/test_hmac.rb +34 -0
  98. data/test/transport/test_identity_cipher.rb +40 -0
  99. data/test/transport/test_packet_stream.rb +433 -0
  100. data/test/transport/test_server_version.rb +55 -0
  101. data/test/transport/test_session.rb +312 -0
  102. data/test/transport/test_state.rb +173 -0
  103. metadata +222 -0
@@ -0,0 +1,262 @@
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
+ @options = options
62
+
63
+ debug { "establishing connection to #{@host}:#{@port}" }
64
+ factory = options[:proxy] || TCPSocket
65
+ @socket = timeout(options[:timeout] || 0) { factory.open(@host, @port) }
66
+ @socket.extend(PacketStream)
67
+ @socket.logger = @logger
68
+
69
+ debug { "connection established" }
70
+
71
+ @queue = []
72
+
73
+ @host_key_verifier = select_host_key_verifier(options[:paranoid])
74
+
75
+ @server_version = ServerVersion.new(socket, logger)
76
+
77
+ @algorithms = Algorithms.new(self, options)
78
+ wait { algorithms.initialized? }
79
+ end
80
+
81
+ # Returns the host (and possibly IP address) in a format compatible with
82
+ # SSH known-host files.
83
+ def host_as_string
84
+ @host_as_string ||= begin
85
+ string = "#{host}"
86
+ string = "[#{string}]:#{port}" if port != DEFAULT_PORT
87
+ if socket.peer_ip != host
88
+ string2 = socket.peer_ip
89
+ string2 = "[#{string2}]:#{port}" if port != DEFAULT_PORT
90
+ string << "," << string2
91
+ end
92
+ string
93
+ end
94
+ end
95
+
96
+ # Cleans up (see PacketStream#cleanup) and closes the underlying socket.
97
+ def close
98
+ socket.cleanup
99
+ socket.close
100
+ end
101
+
102
+ # Returns a new service_request packet for the given service name, ready
103
+ # for sending to the server.
104
+ def service_request(service)
105
+ Net::SSH::Buffer.from(:byte, SERVICE_REQUEST, :string, service)
106
+ end
107
+
108
+ # Requests a rekey operation, and blocks until the operation completes.
109
+ # If a rekey is already pending, this returns immediately, having no
110
+ # effect.
111
+ def rekey!
112
+ if !algorithms.pending?
113
+ algorithms.rekey!
114
+ wait { algorithms.initialized? }
115
+ end
116
+ end
117
+
118
+ # Returns immediately if a rekey is already in process. Otherwise, if a
119
+ # rekey is needed (as indicated by the socket, see PacketStream#if_needs_rekey?)
120
+ # one is performed, causing this method to block until it completes.
121
+ def rekey_as_needed
122
+ return if algorithms.pending?
123
+ socket.if_needs_rekey? { rekey! }
124
+ end
125
+
126
+ # Returns a hash of information about the peer (remote) side of the socket,
127
+ # including :ip, :port, :host, and :canonized (see #host_as_string).
128
+ def peer
129
+ @peer ||= { :ip => socket.peer_ip, :port => @port.to_i, :host => @host, :canonized => host_as_string }
130
+ end
131
+
132
+ # Blocks until a new packet is available to be read, and returns that
133
+ # packet. See #poll_message.
134
+ def next_message
135
+ poll_message(:block)
136
+ end
137
+
138
+ # Tries to read the next packet from the socket. If mode is :nonblock (the
139
+ # default), this will not block and will return nil if there are no packets
140
+ # waiting to be read. Otherwise, this will block until a packet is
141
+ # available. Note that some packet types (DISCONNECT, IGNORE, UNIMPLEMENTED,
142
+ # DEBUG, and KEXINIT) are handled silently by this method, and will never
143
+ # be returned.
144
+ #
145
+ # If a key-exchange is in process and a disallowed packet type is
146
+ # received, it will be enqueued and otherwise ignored. When a key-exchange
147
+ # is not in process, and consume_queue is true, packets will be first
148
+ # read from the queue before the socket is queried.
149
+ def poll_message(mode=:nonblock, consume_queue=true)
150
+ loop do
151
+ if consume_queue && @queue.any? && algorithms.allow?(@queue.first)
152
+ return @queue.shift
153
+ end
154
+
155
+ packet = socket.next_packet(mode)
156
+ return nil if packet.nil?
157
+
158
+ case packet.type
159
+ when DISCONNECT
160
+ raise Net::SSH::Disconnect, "disconnected: #{packet[:description]} (#{packet[:reason_code]})"
161
+
162
+ when IGNORE
163
+ debug { "IGNORE packet recieved: #{packet[:data].inspect}" }
164
+
165
+ when UNIMPLEMENTED
166
+ lwarn { "UNIMPLEMENTED: #{packet[:number]}" }
167
+
168
+ when DEBUG
169
+ send(packet[:always_display] ? :fatal : :debug) { packet[:message] }
170
+
171
+ when KEXINIT
172
+ algorithms.accept_kexinit(packet)
173
+
174
+ else
175
+ return packet if algorithms.allow?(packet)
176
+ push(packet)
177
+ end
178
+ end
179
+ end
180
+
181
+ # Waits (blocks) until the given block returns true. If no block is given,
182
+ # this just waits long enough to see if there are any pending packets. Any
183
+ # packets read are enqueued (see #push).
184
+ def wait
185
+ loop do
186
+ break if block_given? && yield
187
+ message = poll_message(:nonblock, false)
188
+ push(message) if message
189
+ break if !block_given?
190
+ end
191
+ end
192
+
193
+ # Adds the given packet to the packet queue. If the queue is non-empty,
194
+ # #poll_message will return packets from the queue in the order they
195
+ # were received.
196
+ def push(packet)
197
+ @queue.push(packet)
198
+ end
199
+
200
+ # Sends the given message via the packet stream, blocking until the
201
+ # entire message has been sent.
202
+ def send_message(message)
203
+ socket.send_packet(message)
204
+ end
205
+
206
+ # Enqueues the given message, such that it will be sent at the earliest
207
+ # opportunity. This does not block, but returns immediately.
208
+ def enqueue_message(message)
209
+ socket.enqueue_packet(message)
210
+ end
211
+
212
+ # Configure's the packet stream's client state with the given set of
213
+ # options. This is typically used to define the cipher, compression, and
214
+ # hmac algorithms to use when sending packets to the server.
215
+ def configure_client(options={})
216
+ socket.client.set(options)
217
+ end
218
+
219
+ # Configure's the packet stream's server state with the given set of
220
+ # options. This is typically used to define the cipher, compression, and
221
+ # hmac algorithms to use when reading packets from the server.
222
+ def configure_server(options={})
223
+ socket.server.set(options)
224
+ end
225
+
226
+ # Sets a new hint for the packet stream, which the packet stream may use
227
+ # to change its behavior. (See PacketStream#hints).
228
+ def hint(which, value=true)
229
+ socket.hints[which] = value
230
+ end
231
+
232
+ public
233
+
234
+ # this method is primarily for use in tests
235
+ attr_reader :queue #:nodoc:
236
+
237
+ private
238
+
239
+ # Instantiates a new host-key verification class, based on the value of
240
+ # the parameter. When true or nil, the default Lenient verifier is
241
+ # returned. If it is false, the Null verifier is returned, and if it is
242
+ # :very, the Strict verifier is returned. If the argument happens to
243
+ # respond to :verify, it is returned directly. Otherwise, an exception
244
+ # is raised.
245
+ def select_host_key_verifier(paranoid)
246
+ case paranoid
247
+ when true, nil then
248
+ Net::SSH::Verifiers::Lenient.new
249
+ when false then
250
+ Net::SSH::Verifiers::Null.new
251
+ when :very then
252
+ Net::SSH::Verifiers::Strict.new
253
+ else
254
+ if paranoid.respond_to?(:verify)
255
+ paranoid
256
+ else
257
+ raise ArgumentError, "argument to :paranoid is not valid: #{paranoid.inspect}"
258
+ end
259
+ end
260
+ end
261
+ end
262
+ end; end; end
@@ -0,0 +1,170 @@
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 cipher algorithm in use for this socket endpoint.
20
+ attr_reader :cipher
21
+
22
+ # The hmac algorithm in use for this endpoint.
23
+ attr_reader :hmac
24
+
25
+ # The compression algorithm in use for this endpoint.
26
+ attr_reader :compression
27
+
28
+ # The compression level to use when compressing data (or nil, for the default).
29
+ attr_reader :compression_level
30
+
31
+ # The number of packets processed since the last call to #reset!
32
+ attr_reader :packets
33
+
34
+ # The number of data blocks processed since the last call to #reset!
35
+ attr_reader :blocks
36
+
37
+ # The maximum number of packets that this endpoint wants to process before
38
+ # needing a rekey.
39
+ attr_accessor :max_packets
40
+
41
+ # The maximum number of blocks that this endpoint wants to process before
42
+ # needing a rekey.
43
+ attr_accessor :max_blocks
44
+
45
+ # The user-specified maximum number of bytes that this endpoint ought to
46
+ # process before needing a rekey.
47
+ attr_accessor :rekey_limit
48
+
49
+ # Creates a new state object, belonging to the given socket. Initializes
50
+ # the algorithms to "none".
51
+ def initialize(socket)
52
+ @socket = socket
53
+ @sequence_number = @packets = @blocks = 0
54
+ @cipher = CipherFactory.get("none")
55
+ @hmac = HMAC.get("none")
56
+ @compression = nil
57
+ @compressor = @decompressor = nil
58
+ end
59
+
60
+ # A convenience method for quickly setting multiple values in a single
61
+ # command.
62
+ def set(values)
63
+ values.each do |key, value|
64
+ instance_variable_set("@#{key}", value)
65
+ end
66
+ reset!
67
+ end
68
+
69
+ # Increments the counters. The sequence number is incremented (and remapped
70
+ # so it always fits in a 32-bit integer). The number of packets and blocks
71
+ # are also incremented.
72
+ def increment(packet_length)
73
+ @sequence_number = (@sequence_number + 1) & 0xFFFFFFFF
74
+ @packets += 1
75
+ @blocks += (packet_length + 4) / cipher.block_size
76
+ end
77
+
78
+ # The compressor object to use when compressing data. This takes into account
79
+ # the desired compression level.
80
+ def compressor
81
+ @compressor ||= Zlib::Deflate.new(compression_level || Zlib::DEFAULT_COMPRESSION)
82
+ end
83
+
84
+ # The decompressor object to use when decompressing data.
85
+ def decompressor
86
+ @decompressor ||= Zlib::Inflate.new(nil)
87
+ end
88
+
89
+ # Returns true if data compression/decompression is enabled. This will
90
+ # return true if :standard compression is selected, or if :delayed
91
+ # compression is selected and the :authenticated hint has been received
92
+ # by the socket.
93
+ def compression?
94
+ compression == :standard || (compression == :delayed && socket.hints[:authenticated])
95
+ end
96
+
97
+ # Compresses the data. If no compression is in effect, this will just return
98
+ # the data unmodified, otherwise it uses #compressor to compress the data.
99
+ def compress(data)
100
+ data = data.to_s
101
+ return data unless compression?
102
+ compressor.deflate(data, Zlib::SYNC_FLUSH)
103
+ end
104
+
105
+ # Deompresses the data. If no compression is in effect, this will just return
106
+ # the data unmodified, otherwise it uses #decompressor to decompress the data.
107
+ def decompress(data)
108
+ data = data.to_s
109
+ return data unless compression?
110
+ decompressor.inflate(data)
111
+ end
112
+
113
+ # Resets the counters on the state object, but leaves the sequence_number
114
+ # unchanged. It also sets defaults for and recomputes the max_packets and
115
+ # max_blocks values.
116
+ def reset!
117
+ @packets = @blocks = 0
118
+
119
+ @max_packets ||= 1 << 31
120
+
121
+ if max_blocks.nil?
122
+ # cargo-culted from openssh. the idea is that "the 2^(blocksize*2)
123
+ # limit is too expensive for 3DES, blowfish, etc., so enforce a 1GB
124
+ # limit for small blocksizes."
125
+
126
+ if cipher.block_size >= 16
127
+ @max_blocks = 1 << (cipher.block_size * 2)
128
+ else
129
+ @max_blocks = (1 << 30) / cipher.block_size
130
+ end
131
+
132
+ # if a limit on the # of bytes has been given, convert that into a
133
+ # minimum number of blocks processed.
134
+
135
+ if rekey_limit
136
+ @max_blocks = [@max_blocks, rekey_limit / cipher.block_size].min
137
+ end
138
+ end
139
+
140
+ cleanup
141
+ end
142
+
143
+ # Closes any the compressor and/or decompressor objects that have been
144
+ # instantiated.
145
+ def cleanup
146
+ if @compressor
147
+ @compressor.finish if !@compressor.finished?
148
+ @compressor.close
149
+ end
150
+
151
+ if @decompressor
152
+ # we call reset here so that we don't get warnings when we try to
153
+ # close the decompressor
154
+ @decompressor.reset
155
+ @decompressor.close
156
+ end
157
+
158
+ @compressor = @decompressor = nil
159
+ end
160
+
161
+ # Returns true if the number of packets processed exceeds the maximum
162
+ # number of packets, or if the number of blocks processed exceeds the
163
+ # maximum number of blocks.
164
+ def needs_rekey?
165
+ max_packets && packets > max_packets ||
166
+ max_blocks && blocks > max_blocks
167
+ end
168
+ end
169
+
170
+ end; end; end
@@ -0,0 +1,30 @@
1
+ require 'net/ssh/verifiers/strict'
2
+
3
+ module Net; module SSH; module Verifiers
4
+
5
+ # Basically the same as the Strict verifier, but does not try to actually
6
+ # verify a connection if the server is the localhost and the port is a
7
+ # nonstandard port number. Those two conditions will typically mean the
8
+ # connection is being tunnelled through a forwarded port, so the known-hosts
9
+ # file will not be helpful (in general).
10
+ class Lenient < Strict
11
+ # Tries to determine if the connection is being tunnelled, and if so,
12
+ # returns true. Otherwise, performs the standard strict verification.
13
+ def verify(arguments)
14
+ return true if tunnelled?(arguments)
15
+ super
16
+ end
17
+
18
+ private
19
+
20
+ # A connection is potentially being tunnelled if the port is not 22,
21
+ # and the ip refers to the localhost.
22
+ def tunnelled?(args)
23
+ return false if args[:session].port == Net::SSH::Transport::Session::DEFAULT_PORT
24
+
25
+ ip = args[:session].peer[:ip]
26
+ return ip == "127.0.0.1" || ip == "::1"
27
+ end
28
+ end
29
+
30
+ end; end; end