net-ssh-net-ssh 2.0.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. data/CHANGELOG.rdoc +137 -0
  2. data/Manifest +104 -0
  3. data/README.rdoc +110 -0
  4. data/Rakefile +79 -0
  5. data/THANKS.rdoc +16 -0
  6. data/lib/net/ssh.rb +215 -0
  7. data/lib/net/ssh/authentication/agent.rb +176 -0
  8. data/lib/net/ssh/authentication/constants.rb +18 -0
  9. data/lib/net/ssh/authentication/key_manager.rb +193 -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 +183 -0
  16. data/lib/net/ssh/authentication/session.rb +134 -0
  17. data/lib/net/ssh/buffer.rb +340 -0
  18. data/lib/net/ssh/buffered_io.rb +149 -0
  19. data/lib/net/ssh/config.rb +181 -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 +596 -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 +102 -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 +129 -0
  34. data/lib/net/ssh/ruby_compat.rb +7 -0
  35. data/lib/net/ssh/service/forward.rb +267 -0
  36. data/lib/net/ssh/test.rb +89 -0
  37. data/lib/net/ssh/test/channel.rb +129 -0
  38. data/lib/net/ssh/test/extensions.rb +152 -0
  39. data/lib/net/ssh/test/kex.rb +44 -0
  40. data/lib/net/ssh/test/local_packet.rb +51 -0
  41. data/lib/net/ssh/test/packet.rb +81 -0
  42. data/lib/net/ssh/test/remote_packet.rb +38 -0
  43. data/lib/net/ssh/test/script.rb +157 -0
  44. data/lib/net/ssh/test/socket.rb +59 -0
  45. data/lib/net/ssh/transport/algorithms.rb +384 -0
  46. data/lib/net/ssh/transport/cipher_factory.rb +84 -0
  47. data/lib/net/ssh/transport/constants.rb +30 -0
  48. data/lib/net/ssh/transport/hmac.rb +31 -0
  49. data/lib/net/ssh/transport/hmac/abstract.rb +78 -0
  50. data/lib/net/ssh/transport/hmac/md5.rb +12 -0
  51. data/lib/net/ssh/transport/hmac/md5_96.rb +11 -0
  52. data/lib/net/ssh/transport/hmac/none.rb +15 -0
  53. data/lib/net/ssh/transport/hmac/sha1.rb +13 -0
  54. data/lib/net/ssh/transport/hmac/sha1_96.rb +11 -0
  55. data/lib/net/ssh/transport/identity_cipher.rb +55 -0
  56. data/lib/net/ssh/transport/kex.rb +13 -0
  57. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +208 -0
  58. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +77 -0
  59. data/lib/net/ssh/transport/openssl.rb +128 -0
  60. data/lib/net/ssh/transport/packet_stream.rb +230 -0
  61. data/lib/net/ssh/transport/server_version.rb +69 -0
  62. data/lib/net/ssh/transport/session.rb +276 -0
  63. data/lib/net/ssh/transport/state.rb +206 -0
  64. data/lib/net/ssh/verifiers/lenient.rb +30 -0
  65. data/lib/net/ssh/verifiers/null.rb +12 -0
  66. data/lib/net/ssh/verifiers/strict.rb +53 -0
  67. data/lib/net/ssh/version.rb +62 -0
  68. data/net-ssh.gemspec +128 -0
  69. data/setup.rb +1585 -0
  70. data/test/authentication/methods/common.rb +28 -0
  71. data/test/authentication/methods/test_abstract.rb +51 -0
  72. data/test/authentication/methods/test_hostbased.rb +114 -0
  73. data/test/authentication/methods/test_keyboard_interactive.rb +98 -0
  74. data/test/authentication/methods/test_password.rb +50 -0
  75. data/test/authentication/methods/test_publickey.rb +127 -0
  76. data/test/authentication/test_agent.rb +205 -0
  77. data/test/authentication/test_key_manager.rb +105 -0
  78. data/test/authentication/test_session.rb +93 -0
  79. data/test/common.rb +106 -0
  80. data/test/configs/eqsign +3 -0
  81. data/test/configs/exact_match +8 -0
  82. data/test/configs/wild_cards +14 -0
  83. data/test/connection/test_channel.rb +452 -0
  84. data/test/connection/test_session.rb +488 -0
  85. data/test/test_all.rb +6 -0
  86. data/test/test_buffer.rb +336 -0
  87. data/test/test_buffered_io.rb +63 -0
  88. data/test/test_config.rb +84 -0
  89. data/test/test_key_factory.rb +67 -0
  90. data/test/transport/hmac/test_md5.rb +39 -0
  91. data/test/transport/hmac/test_md5_96.rb +25 -0
  92. data/test/transport/hmac/test_none.rb +34 -0
  93. data/test/transport/hmac/test_sha1.rb +34 -0
  94. data/test/transport/hmac/test_sha1_96.rb +25 -0
  95. data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +146 -0
  96. data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +92 -0
  97. data/test/transport/test_algorithms.rb +302 -0
  98. data/test/transport/test_cipher_factory.rb +171 -0
  99. data/test/transport/test_hmac.rb +34 -0
  100. data/test/transport/test_identity_cipher.rb +40 -0
  101. data/test/transport/test_packet_stream.rb +435 -0
  102. data/test/transport/test_server_version.rb +68 -0
  103. data/test/transport/test_session.rb +315 -0
  104. data/test/transport/test_state.rb +173 -0
  105. metadata +162 -0
@@ -0,0 +1,134 @@
1
+ require 'net/ssh/loggable'
2
+ require 'net/ssh/transport/constants'
3
+ require 'net/ssh/authentication/constants'
4
+ require 'net/ssh/authentication/key_manager'
5
+ require 'net/ssh/authentication/methods/publickey'
6
+ require 'net/ssh/authentication/methods/hostbased'
7
+ require 'net/ssh/authentication/methods/password'
8
+ require 'net/ssh/authentication/methods/keyboard_interactive'
9
+
10
+ module Net; module SSH; module Authentication
11
+
12
+ # Represents an authentication session. It manages the authentication of
13
+ # a user over an established connection (the "transport" object, see
14
+ # Net::SSH::Transport::Session).
15
+ #
16
+ # The use of an authentication session to manage user authentication is
17
+ # internal to Net::SSH (specifically Net::SSH.start). Consumers of the
18
+ # Net::SSH library will never need to access this class directly.
19
+ class Session
20
+ include Transport::Constants, Constants, Loggable
21
+
22
+ # transport layer abstraction
23
+ attr_reader :transport
24
+
25
+ # the list of authentication methods to try
26
+ attr_reader :auth_methods
27
+
28
+ # the list of authentication methods that are allowed
29
+ attr_reader :allowed_auth_methods
30
+
31
+ # a hash of options, given at construction time
32
+ attr_reader :options
33
+
34
+ # Instantiates a new Authentication::Session object over the given
35
+ # transport layer abstraction.
36
+ def initialize(transport, options={})
37
+ self.logger = transport.logger
38
+ @transport = transport
39
+
40
+ @auth_methods = options[:auth_methods] || %w(publickey hostbased password keyboard-interactive)
41
+ @options = options
42
+
43
+ @allowed_auth_methods = @auth_methods
44
+ end
45
+
46
+ # Attempts to authenticate the given user, in preparation for the next
47
+ # service request. Returns true if an authentication method succeeds in
48
+ # authenticating the user, and false otherwise.
49
+ def authenticate(next_service, username, password=nil)
50
+ debug { "beginning authentication of `#{username}'" }
51
+
52
+ transport.send_message(transport.service_request("ssh-userauth"))
53
+ message = expect_message(SERVICE_ACCEPT)
54
+
55
+ key_manager = KeyManager.new(logger, options)
56
+ keys.each { |key| key_manager.add(key) } unless keys.empty?
57
+ key_data.each { |key2| key_manager.add_key_data(key2) } unless key_data.empty?
58
+
59
+ attempted = []
60
+
61
+ @auth_methods.each do |name|
62
+ next unless @allowed_auth_methods.include?(name)
63
+ attempted << name
64
+
65
+ debug { "trying #{name}" }
66
+ method = Methods.const_get(name.split(/\W+/).map { |p| p.capitalize }.join).new(self, :key_manager => key_manager)
67
+
68
+ return true if method.authenticate(next_service, username, password)
69
+ end
70
+
71
+ error { "all authorization methods failed (tried #{attempted.join(', ')})" }
72
+ return false
73
+ ensure
74
+ key_manager.finish if key_manager
75
+ end
76
+
77
+ # Blocks until a packet is received. It silently handles USERAUTH_BANNER
78
+ # packets, and will raise an error if any packet is received that is not
79
+ # valid during user authentication.
80
+ def next_message
81
+ loop do
82
+ packet = transport.next_message
83
+
84
+ case packet.type
85
+ when USERAUTH_BANNER
86
+ info { packet[:message] }
87
+ # TODO add a hook for people to retrieve the banner when it is sent
88
+
89
+ when USERAUTH_FAILURE
90
+ @allowed_auth_methods = packet[:authentications].split(/,/)
91
+ debug { "allowed methods: #{packet[:authentications]}" }
92
+ return packet
93
+
94
+ when USERAUTH_METHOD_RANGE, SERVICE_ACCEPT
95
+ return packet
96
+
97
+ when USERAUTH_SUCCESS
98
+ transport.hint :authenticated
99
+ return packet
100
+
101
+ else
102
+ raise Net::SSH::Exception, "unexpected message #{packet.type} (#{packet})"
103
+ end
104
+ end
105
+ end
106
+
107
+ # Blocks until a packet is received, and returns it if it is of the given
108
+ # type. If it is not, an exception is raised.
109
+ def expect_message(type)
110
+ message = next_message
111
+ unless message.type == type
112
+ raise Net::SSH::Exception, "expected #{type}, got #{message.type} (#{message})"
113
+ end
114
+ message
115
+ end
116
+
117
+ private
118
+
119
+ # Returns an array of paths to the key files that should be used when
120
+ # attempting any key-based authentication mechanism.
121
+ def keys
122
+ Array(
123
+ options[:keys] ||
124
+ %w(~/.ssh/id_dsa ~/.ssh/id_rsa ~/.ssh2/id_dsa ~/.ssh2/id_rsa)
125
+ )
126
+ end
127
+
128
+ # Returns an array of the key data that should be used when
129
+ # attempting any key-based authentication mechanism.
130
+ def key_data
131
+ Array(options[:key_data])
132
+ end
133
+ end
134
+ end; end; end
@@ -0,0 +1,340 @@
1
+ require 'net/ssh/ruby_compat'
2
+ require 'net/ssh/transport/openssl'
3
+
4
+ module Net; module SSH
5
+
6
+ # Net::SSH::Buffer is a flexible class for building and parsing binary
7
+ # data packets. It provides a stream-like interface for sequentially
8
+ # reading data items from the buffer, as well as a useful helper method
9
+ # for building binary packets given a signature.
10
+ #
11
+ # Writing to a buffer always appends to the end, regardless of where the
12
+ # read cursor is. Reading, on the other hand, always begins at the first
13
+ # byte of the buffer and increments the read cursor, with subsequent reads
14
+ # taking up where the last left off.
15
+ #
16
+ # As a consumer of the Net::SSH library, you will rarely come into contact
17
+ # with these buffer objects directly, but it could happen. Also, if you
18
+ # are ever implementing a protocol on top of SSH (e.g. SFTP), this buffer
19
+ # class can be quite handy.
20
+ class Buffer
21
+ # This is a convenience method for creating and populating a new buffer
22
+ # from a single command. The arguments must be even in length, with the
23
+ # first of each pair of arguments being a symbol naming the type of the
24
+ # data that follows. If the type is :raw, the value is written directly
25
+ # to the hash.
26
+ #
27
+ # b = Buffer.from(:byte, 1, :string, "hello", :raw, "\1\2\3\4")
28
+ # #-> "\1\0\0\0\5hello\1\2\3\4"
29
+ #
30
+ # The supported data types are:
31
+ #
32
+ # * :raw => write the next value verbatim (#write)
33
+ # * :int64 => write an 8-byte integer (#write_int64)
34
+ # * :long => write a 4-byte integer (#write_long)
35
+ # * :byte => write a single byte (#write_byte)
36
+ # * :string => write a 4-byte length followed by character data (#write_string)
37
+ # * :bool => write a single byte, interpreted as a boolean (#write_bool)
38
+ # * :bignum => write an SSH-encoded bignum (#write_bignum)
39
+ # * :key => write an SSH-encoded key value (#write_key)
40
+ #
41
+ # Any of these, except for :raw, accepts an Array argument, to make it
42
+ # easier to write multiple values of the same type in a briefer manner.
43
+ def self.from(*args)
44
+ raise ArgumentError, "odd number of arguments given" unless args.length % 2 == 0
45
+
46
+ buffer = new
47
+ 0.step(args.length-1, 2) do |index|
48
+ type = args[index]
49
+ value = args[index+1]
50
+ if type == :raw
51
+ buffer.append(value.to_s)
52
+ elsif Array === value
53
+ buffer.send("write_#{type}", *value)
54
+ else
55
+ buffer.send("write_#{type}", value)
56
+ end
57
+ end
58
+
59
+ buffer
60
+ end
61
+
62
+ # exposes the raw content of the buffer
63
+ attr_reader :content
64
+
65
+ # the current position of the pointer in the buffer
66
+ attr_accessor :position
67
+
68
+ # Creates a new buffer, initialized to the given content. The position
69
+ # is initialized to the beginning of the buffer.
70
+ def initialize(content="")
71
+ @content = content.to_s
72
+ @position = 0
73
+ end
74
+
75
+ # Returns the length of the buffer's content.
76
+ def length
77
+ @content.length
78
+ end
79
+
80
+ # Returns the number of bytes available to be read (e.g., how many bytes
81
+ # remain between the current position and the end of the buffer).
82
+ def available
83
+ length - position
84
+ end
85
+
86
+ # Returns a copy of the buffer's content.
87
+ def to_s
88
+ (@content || "").dup
89
+ end
90
+
91
+ # Compares the contents of the two buffers, returning +true+ only if they
92
+ # are identical in size and content.
93
+ def ==(buffer)
94
+ to_s == buffer.to_s
95
+ end
96
+
97
+ # Returns +true+ if the buffer contains no data (e.g., it is of zero length).
98
+ def empty?
99
+ @content.empty?
100
+ end
101
+
102
+ # Resets the pointer to the start of the buffer. Subsequent reads will
103
+ # begin at position 0.
104
+ def reset!
105
+ @position = 0
106
+ end
107
+
108
+ # Returns true if the pointer is at the end of the buffer. Subsequent
109
+ # reads will return nil, in this case.
110
+ def eof?
111
+ @position >= length
112
+ end
113
+
114
+ # Resets the buffer, making it empty. Also, resets the read position to
115
+ # 0.
116
+ def clear!
117
+ @content = ""
118
+ @position = 0
119
+ end
120
+
121
+ # Consumes n bytes from the buffer, where n is the current position
122
+ # unless otherwise specified. This is useful for removing data from the
123
+ # buffer that has previously been read, when you are expecting more data
124
+ # to be appended. It helps to keep the size of buffers down when they
125
+ # would otherwise tend to grow without bound.
126
+ #
127
+ # Returns the buffer object itself.
128
+ def consume!(n=position)
129
+ if n >= length
130
+ # optimize for a fairly common case
131
+ clear!
132
+ elsif n > 0
133
+ @content = @content[n..-1] || ""
134
+ @position -= n
135
+ @position = 0 if @position < 0
136
+ end
137
+ self
138
+ end
139
+
140
+ # Appends the given text to the end of the buffer. Does not alter the
141
+ # read position. Returns the buffer object itself.
142
+ def append(text)
143
+ @content << text
144
+ self
145
+ end
146
+
147
+ # Returns all text from the current pointer to the end of the buffer as
148
+ # a new Net::SSH::Buffer object.
149
+ def remainder_as_buffer
150
+ Buffer.new(@content[@position..-1])
151
+ end
152
+
153
+ # Reads all data up to and including the given pattern, which may be a
154
+ # String, Fixnum, or Regexp and is interpreted exactly as String#index
155
+ # does. Returns nil if nothing matches. Increments the position to point
156
+ # immediately after the pattern, if it does match. Returns all data up to
157
+ # and including the text that matched the pattern.
158
+ def read_to(pattern)
159
+ index = @content.index(pattern, @position) or return nil
160
+ length = case pattern
161
+ when String then pattern.length
162
+ when Fixnum then 1
163
+ when Regexp then $&.length
164
+ end
165
+ index && read(index+length)
166
+ end
167
+
168
+ # Reads and returns the next +count+ bytes from the buffer, starting from
169
+ # the read position. If +count+ is +nil+, this will return all remaining
170
+ # text in the buffer. This method will increment the pointer.
171
+ def read(count=nil)
172
+ count ||= length
173
+ count = length - @position if @position + count > length
174
+ @position += count
175
+ @content[@position-count, count]
176
+ end
177
+
178
+ # Reads (as #read) and returns the given number of bytes from the buffer,
179
+ # and then consumes (as #consume!) all data up to the new read position.
180
+ def read!(count=nil)
181
+ data = read(count)
182
+ consume!
183
+ data
184
+ end
185
+
186
+ # Return the next 8 bytes as a 64-bit integer (in network byte order).
187
+ # Returns nil if there are less than 8 bytes remaining to be read in the
188
+ # buffer.
189
+ def read_int64
190
+ hi = read_long or return nil
191
+ lo = read_long or return nil
192
+ return (hi << 32) + lo
193
+ end
194
+
195
+ # Return the next four bytes as a long integer (in network byte order).
196
+ # Returns nil if there are less than 4 bytes remaining to be read in the
197
+ # buffer.
198
+ def read_long
199
+ b = read(4) or return nil
200
+ b.unpack("N").first
201
+ end
202
+
203
+ # Read and return the next byte in the buffer. Returns nil if called at
204
+ # the end of the buffer.
205
+ def read_byte
206
+ b = read(1) or return nil
207
+ b.getbyte(0)
208
+ end
209
+
210
+ # Read and return an SSH2-encoded string. The string starts with a long
211
+ # integer that describes the number of bytes remaining in the string.
212
+ # Returns nil if there are not enough bytes to satisfy the request.
213
+ def read_string
214
+ length = read_long or return nil
215
+ read(length)
216
+ end
217
+
218
+ # Read a single byte and convert it into a boolean, using 'C' rules
219
+ # (i.e., zero is false, non-zero is true).
220
+ def read_bool
221
+ b = read_byte or return nil
222
+ b != 0
223
+ end
224
+
225
+ # Read a bignum (OpenSSL::BN) from the buffer, in SSH2 format. It is
226
+ # essentially just a string, which is reinterpreted to be a bignum in
227
+ # binary format.
228
+ def read_bignum
229
+ data = read_string
230
+ return unless data
231
+ OpenSSL::BN.new(data, 2)
232
+ end
233
+
234
+ # Read a key from the buffer. The key will start with a string
235
+ # describing its type. The remainder of the key is defined by the
236
+ # type that was read.
237
+ def read_key
238
+ type = read_string
239
+ return (type ? read_keyblob(type) : nil)
240
+ end
241
+
242
+ # Read a keyblob of the given type from the buffer, and return it as
243
+ # a key. Only RSA and DSA keys are supported.
244
+ def read_keyblob(type)
245
+ case type
246
+ when "ssh-dss"
247
+ key = OpenSSL::PKey::DSA.new
248
+ key.p = read_bignum
249
+ key.q = read_bignum
250
+ key.g = read_bignum
251
+ key.pub_key = read_bignum
252
+
253
+ when "ssh-rsa"
254
+ key = OpenSSL::PKey::RSA.new
255
+ key.e = read_bignum
256
+ key.n = read_bignum
257
+
258
+ else
259
+ raise NotImplementedError, "unsupported key type `#{type}'"
260
+ end
261
+
262
+ return key
263
+ end
264
+
265
+ # Reads the next string from the buffer, and returns a new Buffer
266
+ # object that wraps it.
267
+ def read_buffer
268
+ Buffer.new(read_string)
269
+ end
270
+
271
+ # Writes the given data literally into the string. Does not alter the
272
+ # read position. Returns the buffer object.
273
+ def write(*data)
274
+ data.each { |datum| @content << datum }
275
+ self
276
+ end
277
+
278
+ # Writes each argument to the buffer as a network-byte-order-encoded
279
+ # 64-bit integer (8 bytes). Does not alter the read position. Returns the
280
+ # buffer object.
281
+ def write_int64(*n)
282
+ n.each do |i|
283
+ hi = (i >> 32) & 0xFFFFFFFF
284
+ lo = i & 0xFFFFFFFF
285
+ @content << [hi, lo].pack("N2")
286
+ end
287
+ self
288
+ end
289
+
290
+ # Writes each argument to the buffer as a network-byte-order-encoded
291
+ # long (4-byte) integer. Does not alter the read position. Returns the
292
+ # buffer object.
293
+ def write_long(*n)
294
+ @content << n.pack("N*")
295
+ self
296
+ end
297
+
298
+ # Writes each argument to the buffer as a byte. Does not alter the read
299
+ # position. Returns the buffer object.
300
+ def write_byte(*n)
301
+ n.each { |b| @content << b.chr }
302
+ self
303
+ end
304
+
305
+ # Writes each argument to the buffer as an SSH2-encoded string. Each
306
+ # string is prefixed by its length, encoded as a 4-byte long integer.
307
+ # Does not alter the read position. Returns the buffer object.
308
+ def write_string(*text)
309
+ text.each do |string|
310
+ s = string.to_s
311
+ write_long(s.length)
312
+ write(s)
313
+ end
314
+ self
315
+ end
316
+
317
+ # Writes each argument to the buffer as a (C-style) boolean, with 1
318
+ # meaning true, and 0 meaning false. Does not alter the read position.
319
+ # Returns the buffer object.
320
+ def write_bool(*b)
321
+ b.each { |v| @content << (v ? "\1" : "\0") }
322
+ self
323
+ end
324
+
325
+ # Writes each argument to the buffer as a bignum (SSH2-style). No
326
+ # checking is done to ensure that the arguments are, in fact, bignums.
327
+ # Does not alter the read position. Returns the buffer object.
328
+ def write_bignum(*n)
329
+ @content << n.map { |b| b.to_ssh }.join
330
+ self
331
+ end
332
+
333
+ # Writes the given arguments to the buffer as SSH2-encoded keys. Does not
334
+ # alter the read position. Returns the buffer object.
335
+ def write_key(*key)
336
+ key.each { |k| append(k.to_blob) }
337
+ self
338
+ end
339
+ end
340
+ end; end;