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,339 @@
1
+ require 'net/ssh/transport/openssl'
2
+
3
+ module Net; module SSH
4
+
5
+ # Net::SSH::Buffer is a flexible class for building and parsing binary
6
+ # data packets. It provides a stream-like interface for sequentially
7
+ # reading data items from the buffer, as well as a useful helper method
8
+ # for building binary packets given a signature.
9
+ #
10
+ # Writing to a buffer always appends to the end, regardless of where the
11
+ # read cursor is. Reading, on the other hand, always begins at the first
12
+ # byte of the buffer and increments the read cursor, with subsequent reads
13
+ # taking up where the last left off.
14
+ #
15
+ # As a consumer of the Net::SSH library, you will rarely come into contact
16
+ # with these buffer objects directly, but it could happen. Also, if you
17
+ # are ever implementing a protocol on top of SSH (e.g. SFTP), this buffer
18
+ # class can be quite handy.
19
+ class Buffer
20
+ # This is a convenience method for creating and populating a new buffer
21
+ # from a single command. The arguments must be even in length, with the
22
+ # first of each pair of arguments being a symbol naming the type of the
23
+ # data that follows. If the type is :raw, the value is written directly
24
+ # to the hash.
25
+ #
26
+ # b = Buffer.from(:byte, 1, :string, "hello", :raw, "\1\2\3\4")
27
+ # #-> "\1\0\0\0\5hello\1\2\3\4"
28
+ #
29
+ # The supported data types are:
30
+ #
31
+ # * :raw => write the next value verbatim (#write)
32
+ # * :int64 => write an 8-byte integer (#write_int64)
33
+ # * :long => write a 4-byte integer (#write_long)
34
+ # * :byte => write a single byte (#write_byte)
35
+ # * :string => write a 4-byte length followed by character data (#write_string)
36
+ # * :bool => write a single byte, interpreted as a boolean (#write_bool)
37
+ # * :bignum => write an SSH-encoded bignum (#write_bignum)
38
+ # * :key => write an SSH-encoded key value (#write_key)
39
+ #
40
+ # Any of these, except for :raw, accepts an Array argument, to make it
41
+ # easier to write multiple values of the same type in a briefer manner.
42
+ def self.from(*args)
43
+ raise ArgumentError, "odd number of arguments given" unless args.length % 2 == 0
44
+
45
+ buffer = new
46
+ 0.step(args.length-1, 2) do |index|
47
+ type = args[index]
48
+ value = args[index+1]
49
+ if type == :raw
50
+ buffer.append(value.to_s)
51
+ elsif Array === value
52
+ buffer.send("write_#{type}", *value)
53
+ else
54
+ buffer.send("write_#{type}", value)
55
+ end
56
+ end
57
+
58
+ buffer
59
+ end
60
+
61
+ # exposes the raw content of the buffer
62
+ attr_reader :content
63
+
64
+ # the current position of the pointer in the buffer
65
+ attr_accessor :position
66
+
67
+ # Creates a new buffer, initialized to the given content. The position
68
+ # is initialized to the beginning of the buffer.
69
+ def initialize(content="")
70
+ @content = content.to_s
71
+ @position = 0
72
+ end
73
+
74
+ # Returns the length of the buffer's content.
75
+ def length
76
+ @content.length
77
+ end
78
+
79
+ # Returns the number of bytes available to be read (e.g., how many bytes
80
+ # remain between the current position and the end of the buffer).
81
+ def available
82
+ length - position
83
+ end
84
+
85
+ # Returns a copy of the buffer's content.
86
+ def to_s
87
+ (@content || "").dup
88
+ end
89
+
90
+ # Compares the contents of the two buffers, returning +true+ only if they
91
+ # are identical in size and content.
92
+ def ==(buffer)
93
+ to_s == buffer.to_s
94
+ end
95
+
96
+ # Returns +true+ if the buffer contains no data (e.g., it is of zero length).
97
+ def empty?
98
+ @content.empty?
99
+ end
100
+
101
+ # Resets the pointer to the start of the buffer. Subsequent reads will
102
+ # begin at position 0.
103
+ def reset!
104
+ @position = 0
105
+ end
106
+
107
+ # Returns true if the pointer is at the end of the buffer. Subsequent
108
+ # reads will return nil, in this case.
109
+ def eof?
110
+ @position >= length
111
+ end
112
+
113
+ # Resets the buffer, making it empty. Also, resets the read position to
114
+ # 0.
115
+ def clear!
116
+ @content = ""
117
+ @position = 0
118
+ end
119
+
120
+ # Consumes n bytes from the buffer, where n is the current position
121
+ # unless otherwise specified. This is useful for removing data from the
122
+ # buffer that has previously been read, when you are expecting more data
123
+ # to be appended. It helps to keep the size of buffers down when they
124
+ # would otherwise tend to grow without bound.
125
+ #
126
+ # Returns the buffer object itself.
127
+ def consume!(n=position)
128
+ if n >= length
129
+ # optimize for a fairly common case
130
+ clear!
131
+ elsif n > 0
132
+ @content = @content[n..-1] || ""
133
+ @position -= n
134
+ @position = 0 if @position < 0
135
+ end
136
+ self
137
+ end
138
+
139
+ # Appends the given text to the end of the buffer. Does not alter the
140
+ # read position. Returns the buffer object itself.
141
+ def append(text)
142
+ @content << text
143
+ self
144
+ end
145
+
146
+ # Returns all text from the current pointer to the end of the buffer as
147
+ # a new Net::SSH::Buffer object.
148
+ def remainder_as_buffer
149
+ Buffer.new(@content[@position..-1])
150
+ end
151
+
152
+ # Reads all data up to and including the given pattern, which may be a
153
+ # String, Fixnum, or Regexp and is interpreted exactly as String#index
154
+ # does. Returns nil if nothing matches. Increments the position to point
155
+ # immediately after the pattern, if it does match. Returns all data up to
156
+ # and including the text that matched the pattern.
157
+ def read_to(pattern)
158
+ index = @content.index(pattern, @position) or return nil
159
+ length = case pattern
160
+ when String then pattern.length
161
+ when Fixnum then 1
162
+ when Regexp then $&.length
163
+ end
164
+ index && read(index+length)
165
+ end
166
+
167
+ # Reads and returns the next +count+ bytes from the buffer, starting from
168
+ # the read position. If +count+ is +nil+, this will return all remaining
169
+ # text in the buffer. This method will increment the pointer.
170
+ def read(count=nil)
171
+ count ||= length
172
+ count = length - @position if @position + count > length
173
+ @position += count
174
+ @content[@position-count, count]
175
+ end
176
+
177
+ # Reads (as #read) and returns the given number of bytes from the buffer,
178
+ # and then consumes (as #consume!) all data up to the new read position.
179
+ def read!(count=nil)
180
+ data = read(count)
181
+ consume!
182
+ data
183
+ end
184
+
185
+ # Return the next 8 bytes as a 64-bit integer (in network byte order).
186
+ # Returns nil if there are less than 8 bytes remaining to be read in the
187
+ # buffer.
188
+ def read_int64
189
+ hi = read_long or return nil
190
+ lo = read_long or return nil
191
+ return (hi << 32) + lo
192
+ end
193
+
194
+ # Return the next four bytes as a long integer (in network byte order).
195
+ # Returns nil if there are less than 4 bytes remaining to be read in the
196
+ # buffer.
197
+ def read_long
198
+ b = read(4) or return nil
199
+ b.unpack("N").first
200
+ end
201
+
202
+ # Read and return the next byte in the buffer. Returns nil if called at
203
+ # the end of the buffer.
204
+ def read_byte
205
+ b = read(1) or return nil
206
+ b[0]
207
+ end
208
+
209
+ # Read and return an SSH2-encoded string. The string starts with a long
210
+ # integer that describes the number of bytes remaining in the string.
211
+ # Returns nil if there are not enough bytes to satisfy the request.
212
+ def read_string
213
+ length = read_long or return nil
214
+ read(length)
215
+ end
216
+
217
+ # Read a single byte and convert it into a boolean, using 'C' rules
218
+ # (i.e., zero is false, non-zero is true).
219
+ def read_bool
220
+ b = read_byte or return nil
221
+ b != 0
222
+ end
223
+
224
+ # Read a bignum (OpenSSL::BN) from the buffer, in SSH2 format. It is
225
+ # essentially just a string, which is reinterpreted to be a bignum in
226
+ # binary format.
227
+ def read_bignum
228
+ data = read_string
229
+ return unless data
230
+ OpenSSL::BN.new(data, 2)
231
+ end
232
+
233
+ # Read a key from the buffer. The key will start with a string
234
+ # describing its type. The remainder of the key is defined by the
235
+ # type that was read.
236
+ def read_key
237
+ type = read_string
238
+ return (type ? read_keyblob(type) : nil)
239
+ end
240
+
241
+ # Read a keyblob of the given type from the buffer, and return it as
242
+ # a key. Only RSA and DSA keys are supported.
243
+ def read_keyblob(type)
244
+ case type
245
+ when "ssh-dss"
246
+ key = OpenSSL::PKey::DSA.new
247
+ key.p = read_bignum
248
+ key.q = read_bignum
249
+ key.g = read_bignum
250
+ key.pub_key = read_bignum
251
+
252
+ when "ssh-rsa"
253
+ key = OpenSSL::PKey::RSA.new
254
+ key.e = read_bignum
255
+ key.n = read_bignum
256
+
257
+ else
258
+ raise NotImplementedError, "unsupported key type `#{type}'"
259
+ end
260
+
261
+ return key
262
+ end
263
+
264
+ # Reads the next string from the buffer, and returns a new Buffer
265
+ # object that wraps it.
266
+ def read_buffer
267
+ Buffer.new(read_string)
268
+ end
269
+
270
+ # Writes the given data literally into the string. Does not alter the
271
+ # read position. Returns the buffer object.
272
+ def write(*data)
273
+ data.each { |datum| @content << datum }
274
+ self
275
+ end
276
+
277
+ # Writes each argument to the buffer as a network-byte-order-encoded
278
+ # 64-bit integer (8 bytes). Does not alter the read position. Returns the
279
+ # buffer object.
280
+ def write_int64(*n)
281
+ n.each do |i|
282
+ hi = (i >> 32) & 0xFFFFFFFF
283
+ lo = i & 0xFFFFFFFF
284
+ @content << [hi, lo].pack("N2")
285
+ end
286
+ self
287
+ end
288
+
289
+ # Writes each argument to the buffer as a network-byte-order-encoded
290
+ # long (4-byte) integer. Does not alter the read position. Returns the
291
+ # buffer object.
292
+ def write_long(*n)
293
+ @content << n.pack("N*")
294
+ self
295
+ end
296
+
297
+ # Writes each argument to the buffer as a byte. Does not alter the read
298
+ # position. Returns the buffer object.
299
+ def write_byte(*n)
300
+ n.each { |b| @content << b.chr }
301
+ self
302
+ end
303
+
304
+ # Writes each argument to the buffer as an SSH2-encoded string. Each
305
+ # string is prefixed by its length, encoded as a 4-byte long integer.
306
+ # Does not alter the read position. Returns the buffer object.
307
+ def write_string(*text)
308
+ text.each do |string|
309
+ s = string.to_s
310
+ write_long(s.length)
311
+ write(s)
312
+ end
313
+ self
314
+ end
315
+
316
+ # Writes each argument to the buffer as a (C-style) boolean, with 1
317
+ # meaning true, and 0 meaning false. Does not alter the read position.
318
+ # Returns the buffer object.
319
+ def write_bool(*b)
320
+ b.each { |v| @content << (v ? "\1" : "\0") }
321
+ self
322
+ end
323
+
324
+ # Writes each argument to the buffer as a bignum (SSH2-style). No
325
+ # checking is done to ensure that the arguments are, in fact, bignums.
326
+ # Does not alter the read position. Returns the buffer object.
327
+ def write_bignum(*n)
328
+ @content << n.map { |b| b.to_ssh }.join
329
+ self
330
+ end
331
+
332
+ # Writes the given arguments to the buffer as SSH2-encoded keys. Does not
333
+ # alter the read position. Returns the buffer object.
334
+ def write_key(*key)
335
+ key.each { |k| append(k.to_blob) }
336
+ self
337
+ end
338
+ end
339
+ end; end;
@@ -0,0 +1,149 @@
1
+ require 'net/ssh/buffer'
2
+ require 'net/ssh/loggable'
3
+
4
+ module Net; module SSH
5
+
6
+ # This module is used to extend sockets and other IO objects, to allow
7
+ # them to be buffered for both read and write. This abstraction makes it
8
+ # quite easy to write a select-based event loop
9
+ # (see Net::SSH::Connection::Session#listen_to).
10
+ #
11
+ # The general idea is that instead of calling #read directly on an IO that
12
+ # has been extended with this module, you call #fill (to add pending input
13
+ # to the internal read buffer), and then #read_available (to read from that
14
+ # buffer). Likewise, you don't call #write directly, you call #enqueue to
15
+ # add data to the write buffer, and then #send_pending or #wait_for_pending_sends
16
+ # to actually send the data across the wire.
17
+ #
18
+ # In this way you can easily use the object as an argument to IO.select,
19
+ # calling #fill when it is available for read, or #send_pending when it is
20
+ # available for write, and then call #enqueue and #read_available during
21
+ # the idle times.
22
+ #
23
+ # socket = TCPSocket.new(address, port)
24
+ # socket.extend(Net::SSH::BufferedIo)
25
+ #
26
+ # ssh.listen_to(socket)
27
+ #
28
+ # ssh.loop do
29
+ # if socket.available > 0
30
+ # puts socket.read_available
31
+ # socket.enqueue("response\n")
32
+ # end
33
+ # end
34
+ #
35
+ # Note that this module must be used to extend an instance, and should not
36
+ # be included in a class. If you do want to use it via an include, then you
37
+ # must make sure to invoke the private #initialize_buffered_io method in
38
+ # your class' #initialize method:
39
+ #
40
+ # class Foo < IO
41
+ # include Net::SSH::BufferedIo
42
+ #
43
+ # def initialize
44
+ # initialize_buffered_io
45
+ # # ...
46
+ # end
47
+ # end
48
+ module BufferedIo
49
+ include Loggable
50
+
51
+ # Called when the #extend is called on an object, with this module as the
52
+ # argument. It ensures that the modules instance variables are all properly
53
+ # initialized.
54
+ def self.extended(object) #:nodoc:
55
+ # need to use __send__ because #send is overridden in Socket
56
+ object.__send__(:initialize_buffered_io)
57
+ end
58
+
59
+ # Tries to read up to +n+ bytes of data from the remote end, and appends
60
+ # the data to the input buffer. It returns the number of bytes read, or 0
61
+ # if no data was available to be read.
62
+ def fill(n=8192)
63
+ input.consume!
64
+ data = recv(n)
65
+ debug { "read #{data.length} bytes" }
66
+ input.append(data)
67
+ return data.length
68
+ end
69
+
70
+ # Read up to +length+ bytes from the input buffer. If +length+ is nil,
71
+ # all available data is read from the buffer. (See #available.)
72
+ def read_available(length=nil)
73
+ input.read(length || available)
74
+ end
75
+
76
+ # Returns the number of bytes available to be read from the input buffer.
77
+ # (See #read_available.)
78
+ def available
79
+ input.available
80
+ end
81
+
82
+ # Enqueues data in the output buffer, to be written when #send_pending
83
+ # is called. Note that the data is _not_ sent immediately by this method!
84
+ def enqueue(data)
85
+ output.append(data)
86
+ end
87
+
88
+ # Returns +true+ if there is data waiting in the output buffer, and
89
+ # +false+ otherwise.
90
+ def pending_write?
91
+ output.length > 0
92
+ end
93
+
94
+ # Sends as much of the pending output as possible. Returns +true+ if any
95
+ # data was sent, and +false+ otherwise.
96
+ def send_pending
97
+ if output.length > 0
98
+ sent = send(output.to_s, 0)
99
+ debug { "sent #{sent} bytes" }
100
+ output.consume!(sent)
101
+ return sent > 0
102
+ else
103
+ return false
104
+ end
105
+ end
106
+
107
+ # Calls #send_pending repeatedly, if necessary, blocking until the output
108
+ # buffer is empty.
109
+ def wait_for_pending_sends
110
+ send_pending
111
+ while output.length > 0
112
+ result = IO.select(nil, [self]) or next
113
+ next unless result[1].any?
114
+ send_pending
115
+ end
116
+ end
117
+
118
+ public # these methods are primarily for use in tests
119
+
120
+ def write_buffer #:nodoc:
121
+ output.to_s
122
+ end
123
+
124
+ def read_buffer #:nodoc:
125
+ input.to_s
126
+ end
127
+
128
+ private
129
+
130
+ #--
131
+ # Can't use attr_reader here (after +private+) without incurring the
132
+ # wrath of "ruby -w". We hates it.
133
+ #++
134
+
135
+ def input; @input; end
136
+ def output; @output; end
137
+
138
+ # Initializes the intput and output buffers for this object. This method
139
+ # is called automatically when the module is mixed into an object via
140
+ # Object#extend (see Net::SSH::BufferedIo.extended), but must be called
141
+ # explicitly in the +initialize+ method of any class that uses
142
+ # Module#include to add this module.
143
+ def initialize_buffered_io
144
+ @input = Net::SSH::Buffer.new
145
+ @output = Net::SSH::Buffer.new
146
+ end
147
+ end
148
+
149
+ end; end