net-ssh 0.5.0

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 (179) hide show
  1. data/doc/LICENSE-BSD +27 -0
  2. data/doc/LICENSE-GPL +280 -0
  3. data/doc/LICENSE-RUBY +56 -0
  4. data/doc/README +13 -0
  5. data/doc/manual-html/chapter-1.html +333 -0
  6. data/doc/manual-html/chapter-2.html +455 -0
  7. data/doc/manual-html/chapter-3.html +413 -0
  8. data/doc/manual-html/chapter-4.html +353 -0
  9. data/doc/manual-html/chapter-5.html +393 -0
  10. data/doc/manual-html/chapter-6.html +296 -0
  11. data/doc/manual-html/index.html +217 -0
  12. data/doc/manual-html/manual.css +192 -0
  13. data/doc/manual/chapter.erb +18 -0
  14. data/doc/manual/example.erb +18 -0
  15. data/doc/manual/index.erb +29 -0
  16. data/doc/manual/manual.css +192 -0
  17. data/doc/manual/manual.rb +240 -0
  18. data/doc/manual/manual.yml +67 -0
  19. data/doc/manual/page.erb +87 -0
  20. data/doc/manual/parts/channels_callbacks.txt +32 -0
  21. data/doc/manual/parts/channels_loop.txt +14 -0
  22. data/doc/manual/parts/channels_open.txt +20 -0
  23. data/doc/manual/parts/channels_operations.txt +15 -0
  24. data/doc/manual/parts/channels_types.txt +3 -0
  25. data/doc/manual/parts/channels_what_are.txt +7 -0
  26. data/doc/manual/parts/exec_channels.txt +28 -0
  27. data/doc/manual/parts/exec_open.txt +51 -0
  28. data/doc/manual/parts/exec_popen3.txt +35 -0
  29. data/doc/manual/parts/forward_direct.txt +37 -0
  30. data/doc/manual/parts/forward_handlers.txt +16 -0
  31. data/doc/manual/parts/forward_intro.txt +18 -0
  32. data/doc/manual/parts/forward_local.txt +18 -0
  33. data/doc/manual/parts/forward_remote.txt +14 -0
  34. data/doc/manual/parts/intro_author.txt +1 -0
  35. data/doc/manual/parts/intro_getting.txt +39 -0
  36. data/doc/manual/parts/intro_license.txt +6 -0
  37. data/doc/manual/parts/intro_support.txt +7 -0
  38. data/doc/manual/parts/intro_what_is.txt +7 -0
  39. data/doc/manual/parts/intro_what_is_not.txt +3 -0
  40. data/doc/manual/parts/proxy_http.txt +52 -0
  41. data/doc/manual/parts/proxy_intro.txt +1 -0
  42. data/doc/manual/parts/proxy_socks.txt +23 -0
  43. data/doc/manual/parts/session_key.txt +66 -0
  44. data/doc/manual/parts/session_options.txt +42 -0
  45. data/doc/manual/parts/session_session.txt +14 -0
  46. data/doc/manual/parts/session_start.txt +49 -0
  47. data/doc/manual/tutorial.erb +30 -0
  48. data/examples/channel-demo.rb +81 -0
  49. data/examples/port-forward.rb +51 -0
  50. data/examples/process-demo.rb +91 -0
  51. data/examples/remote-net-port-forward.rb +45 -0
  52. data/examples/remote-port-forward.rb +80 -0
  53. data/examples/tail-demo.rb +49 -0
  54. data/lib/net/ssh.rb +52 -0
  55. data/lib/net/ssh/connection/channel.rb +411 -0
  56. data/lib/net/ssh/connection/constants.rb +47 -0
  57. data/lib/net/ssh/connection/driver.rb +343 -0
  58. data/lib/net/ssh/connection/services.rb +72 -0
  59. data/lib/net/ssh/connection/term.rb +90 -0
  60. data/lib/net/ssh/errors.rb +27 -0
  61. data/lib/net/ssh/proxy/errors.rb +34 -0
  62. data/lib/net/ssh/proxy/http.rb +126 -0
  63. data/lib/net/ssh/proxy/socks4.rb +83 -0
  64. data/lib/net/ssh/proxy/socks5.rb +160 -0
  65. data/lib/net/ssh/service/forward/driver.rb +319 -0
  66. data/lib/net/ssh/service/forward/local-network-handler.rb +74 -0
  67. data/lib/net/ssh/service/forward/remote-network-handler.rb +81 -0
  68. data/lib/net/ssh/service/forward/services.rb +76 -0
  69. data/lib/net/ssh/service/process/driver.rb +153 -0
  70. data/lib/net/ssh/service/process/open.rb +193 -0
  71. data/lib/net/ssh/service/process/popen3.rb +160 -0
  72. data/lib/net/ssh/service/process/services.rb +66 -0
  73. data/lib/net/ssh/service/services.rb +44 -0
  74. data/lib/net/ssh/session.rb +242 -0
  75. data/lib/net/ssh/transport/algorithm-negotiator.rb +267 -0
  76. data/lib/net/ssh/transport/compress/compressor.rb +53 -0
  77. data/lib/net/ssh/transport/compress/decompressor.rb +53 -0
  78. data/lib/net/ssh/transport/compress/none-compressor.rb +39 -0
  79. data/lib/net/ssh/transport/compress/none-decompressor.rb +39 -0
  80. data/lib/net/ssh/transport/compress/services.rb +68 -0
  81. data/lib/net/ssh/transport/compress/zlib-compressor.rb +60 -0
  82. data/lib/net/ssh/transport/compress/zlib-decompressor.rb +52 -0
  83. data/lib/net/ssh/transport/constants.rb +66 -0
  84. data/lib/net/ssh/transport/errors.rb +47 -0
  85. data/lib/net/ssh/transport/identity-cipher.rb +61 -0
  86. data/lib/net/ssh/transport/kex/dh-gex.rb +106 -0
  87. data/lib/net/ssh/transport/kex/dh.rb +231 -0
  88. data/lib/net/ssh/transport/kex/services.rb +60 -0
  89. data/lib/net/ssh/transport/ossl/buffer-factory.rb +52 -0
  90. data/lib/net/ssh/transport/ossl/buffer.rb +87 -0
  91. data/lib/net/ssh/transport/ossl/cipher-factory.rb +98 -0
  92. data/lib/net/ssh/transport/ossl/digest-factory.rb +51 -0
  93. data/lib/net/ssh/transport/ossl/hmac-factory.rb +71 -0
  94. data/lib/net/ssh/transport/ossl/hmac/hmac.rb +62 -0
  95. data/lib/net/ssh/transport/ossl/hmac/md5-96.rb +44 -0
  96. data/lib/net/ssh/transport/ossl/hmac/md5.rb +46 -0
  97. data/lib/net/ssh/transport/ossl/hmac/none.rb +46 -0
  98. data/lib/net/ssh/transport/ossl/hmac/services.rb +68 -0
  99. data/lib/net/ssh/transport/ossl/hmac/sha1-96.rb +44 -0
  100. data/lib/net/ssh/transport/ossl/hmac/sha1.rb +45 -0
  101. data/lib/net/ssh/transport/ossl/key-factory.rb +113 -0
  102. data/lib/net/ssh/transport/ossl/services.rb +149 -0
  103. data/lib/net/ssh/transport/packet-stream.rb +210 -0
  104. data/lib/net/ssh/transport/services.rb +146 -0
  105. data/lib/net/ssh/transport/session.rb +296 -0
  106. data/lib/net/ssh/transport/version-negotiator.rb +73 -0
  107. data/lib/net/ssh/userauth/agent.rb +218 -0
  108. data/lib/net/ssh/userauth/constants.rb +35 -0
  109. data/lib/net/ssh/userauth/driver.rb +176 -0
  110. data/lib/net/ssh/userauth/methods/hostbased.rb +119 -0
  111. data/lib/net/ssh/userauth/methods/password.rb +70 -0
  112. data/lib/net/ssh/userauth/methods/publickey.rb +137 -0
  113. data/lib/net/ssh/userauth/methods/services.rb +63 -0
  114. data/lib/net/ssh/userauth/services.rb +126 -0
  115. data/lib/net/ssh/userauth/userkeys.rb +258 -0
  116. data/lib/net/ssh/util/buffer.rb +274 -0
  117. data/lib/net/ssh/util/openssl.rb +146 -0
  118. data/lib/net/ssh/util/prompter.rb +73 -0
  119. data/lib/net/ssh/version.rb +29 -0
  120. data/test/ALL-TESTS.rb +21 -0
  121. data/test/connection/tc_channel.rb +136 -0
  122. data/test/connection/tc_driver.rb +287 -0
  123. data/test/connection/tc_integration.rb +85 -0
  124. data/test/proxy/tc_http.rb +209 -0
  125. data/test/proxy/tc_socks4.rb +148 -0
  126. data/test/proxy/tc_socks5.rb +214 -0
  127. data/test/service/forward/tc_driver.rb +289 -0
  128. data/test/service/forward/tc_local_network_handler.rb +123 -0
  129. data/test/service/forward/tc_remote_network_handler.rb +108 -0
  130. data/test/service/process/tc_driver.rb +79 -0
  131. data/test/service/process/tc_integration.rb +117 -0
  132. data/test/service/process/tc_open.rb +179 -0
  133. data/test/service/process/tc_popen3.rb +164 -0
  134. data/test/tc_integration.rb +79 -0
  135. data/test/transport/compress/tc_none_compress.rb +41 -0
  136. data/test/transport/compress/tc_none_decompress.rb +45 -0
  137. data/test/transport/compress/tc_zlib_compress.rb +61 -0
  138. data/test/transport/compress/tc_zlib_decompress.rb +48 -0
  139. data/test/transport/kex/tc_dh.rb +304 -0
  140. data/test/transport/kex/tc_dh_gex.rb +70 -0
  141. data/test/transport/ossl/fixtures/dsa-encrypted +15 -0
  142. data/test/transport/ossl/fixtures/dsa-encrypted-bad +15 -0
  143. data/test/transport/ossl/fixtures/dsa-unencrypted +12 -0
  144. data/test/transport/ossl/fixtures/dsa-unencrypted-bad +12 -0
  145. data/test/transport/ossl/fixtures/dsa-unencrypted.pub +1 -0
  146. data/test/transport/ossl/fixtures/not-a-private-key +4 -0
  147. data/test/transport/ossl/fixtures/not-supported +2 -0
  148. data/test/transport/ossl/fixtures/rsa-encrypted +18 -0
  149. data/test/transport/ossl/fixtures/rsa-encrypted-bad +18 -0
  150. data/test/transport/ossl/fixtures/rsa-unencrypted +15 -0
  151. data/test/transport/ossl/fixtures/rsa-unencrypted-bad +15 -0
  152. data/test/transport/ossl/fixtures/rsa-unencrypted.pub +1 -0
  153. data/test/transport/ossl/hmac/tc_hmac.rb +58 -0
  154. data/test/transport/ossl/hmac/tc_md5.rb +50 -0
  155. data/test/transport/ossl/hmac/tc_md5_96.rb +50 -0
  156. data/test/transport/ossl/hmac/tc_none.rb +50 -0
  157. data/test/transport/ossl/hmac/tc_sha1.rb +50 -0
  158. data/test/transport/ossl/hmac/tc_sha1_96.rb +50 -0
  159. data/test/transport/ossl/tc_buffer.rb +97 -0
  160. data/test/transport/ossl/tc_buffer_factory.rb +67 -0
  161. data/test/transport/ossl/tc_cipher_factory.rb +84 -0
  162. data/test/transport/ossl/tc_digest_factory.rb +39 -0
  163. data/test/transport/ossl/tc_hmac_factory.rb +72 -0
  164. data/test/transport/ossl/tc_key_factory.rb +199 -0
  165. data/test/transport/tc_algorithm_negotiator.rb +169 -0
  166. data/test/transport/tc_identity_cipher.rb +52 -0
  167. data/test/transport/tc_integration.rb +110 -0
  168. data/test/transport/tc_packet_stream.rb +183 -0
  169. data/test/transport/tc_session.rb +283 -0
  170. data/test/transport/tc_version_negotiator.rb +86 -0
  171. data/test/userauth/methods/tc_hostbased.rb +136 -0
  172. data/test/userauth/methods/tc_password.rb +89 -0
  173. data/test/userauth/methods/tc_publickey.rb +167 -0
  174. data/test/userauth/tc_agent.rb +223 -0
  175. data/test/userauth/tc_driver.rb +190 -0
  176. data/test/userauth/tc_integration.rb +81 -0
  177. data/test/userauth/tc_userkeys.rb +265 -0
  178. data/test/util/tc_buffer.rb +217 -0
  179. metadata +256 -0
@@ -0,0 +1,210 @@
1
+ #--
2
+ # =============================================================================
3
+ # Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
4
+ # All rights reserved.
5
+ #
6
+ # This source file is distributed as part of the Net::SSH Secure Shell Client
7
+ # library for Ruby. This file (and the library as a whole) may be used only as
8
+ # allowed by either the BSD license, or the Ruby license (or, by association
9
+ # with the Ruby license, the GPL). See the "doc" subdirectory of the Net::SSH
10
+ # distribution for the texts of these licenses.
11
+ # -----------------------------------------------------------------------------
12
+ # net-ssh website : http://net-ssh.rubyforge.org
13
+ # project website: http://rubyforge.org/projects/net-ssh
14
+ # =============================================================================
15
+ #++
16
+
17
+ require 'thread'
18
+
19
+ require 'net/ssh/errors'
20
+ require 'net/ssh/transport/errors'
21
+
22
+ module Net
23
+ module SSH
24
+ module Transport
25
+
26
+ # The abstract parent of IncomingPacketStream and OutgoingPacketStream. It
27
+ # represents the common interface of its subclasses.
28
+ class PacketStream
29
+
30
+ # the sequence number of the next packet to be processed.
31
+ attr_reader :sequence_number
32
+
33
+ # the setter for setting the socket to use for IO communication
34
+ attr_writer :socket
35
+
36
+ # Create a new packet stream. The given ciphers and hmacs are factories
37
+ # that are used to initialize the cipher and mac attributes.
38
+ def initialize( ciphers, hmacs )
39
+ @sequence_number = 0
40
+
41
+ @cipher = ciphers.get( "none" )
42
+ @hmac = hmacs.get( "none" )
43
+ end
44
+
45
+ # Set the cipher and mac algorithms to the given arguments.
46
+ def set_algorithms( cipher, mac )
47
+ @cipher, @hmac = cipher, mac
48
+ end
49
+
50
+ # Compute the mac for the given payload.
51
+ def compute_hmac( payload )
52
+ @hmac.digest( [ @sequence_number, payload ].pack( "NA*" ) )
53
+ end
54
+
55
+ # Increment the sequence number. This handles the (rare) case of a
56
+ # sequence number overflowing a long integer, and resets it safely to 0
57
+ # (as required by the SSH2 protocol).
58
+ def increment_sequence_number
59
+ @sequence_number += 1
60
+ @sequence_number = 0 if @sequence_number > 0xFFFFFFFF
61
+ end
62
+ private :increment_sequence_number
63
+
64
+ end
65
+
66
+ # Handles the compression and encryption of outgoing packets.
67
+ class OutgoingPacketStream < PacketStream
68
+
69
+ # Create a new OutgoingPacketStream.
70
+ def initialize( ciphers, hmacs, compressors )
71
+ super( ciphers, hmacs )
72
+ @compressor = compressors.fetch( "none" )
73
+ @mutex = Mutex.new
74
+ end
75
+
76
+ # Set the cipher, mac, and compressor to the given values.
77
+ def set_algorithms( cipher, hmac, compressor )
78
+ super( cipher, hmac )
79
+ @compressor = compressor
80
+ end
81
+
82
+ # Send the given payload over the socket, after (possibly) compressing
83
+ # and encrypting it. The payload is converted to a string (using #to_s)
84
+ # before being manipulated.
85
+ def send( payload )
86
+ @mutex.synchronize do
87
+ # force the payload into a string
88
+ payload = @compressor.compress( payload.to_s )
89
+
90
+ # the length of the packet, minus the padding
91
+ actual_length = 4 + payload.length + 1
92
+
93
+ # compute the padding length
94
+ padding_length = @cipher.block_size -
95
+ ( actual_length % @cipher.block_size )
96
+ padding_length += @cipher.block_size if padding_length < 4
97
+
98
+ # compute the packet length (sans the length field itself)
99
+ packet_length = payload.length + padding_length + 1
100
+
101
+ if packet_length < 16
102
+ padding_length += @cipher.block_size
103
+ packet_length = payload.length + padding_length + 1
104
+ end
105
+
106
+ padding = Array.new( padding_length ) { rand(256) }.pack("C*")
107
+
108
+ unencrypted_data = [ packet_length, padding_length, payload,
109
+ padding ].pack( "NCA*A*" )
110
+ mac = compute_hmac( unencrypted_data )
111
+
112
+ encrypted_data = @cipher.update( unencrypted_data ) << @cipher.final
113
+ message = encrypted_data + mac
114
+ @socket.send message, 0
115
+
116
+ increment_sequence_number
117
+ end
118
+ end
119
+
120
+ end
121
+
122
+ # Handles the decompression and dencryption of incoming packets.
123
+ class IncomingPacketStream < PacketStream
124
+
125
+ # A handle to the buffer factory to use when creating buffers
126
+ attr_writer :buffers
127
+
128
+ # A handle to the logger instance to use for writing log messages
129
+ attr_writer :log
130
+
131
+ # Create a new IncomingPacketStream.
132
+ def initialize( ciphers, hmacs, decompressors )
133
+ super( ciphers, hmacs )
134
+ @decompressor = decompressors.fetch( "none" )
135
+ @mutex = Mutex.new
136
+ end
137
+
138
+ # Set the cipher, mac, and decompressor algorithms to the given values.
139
+ def set_algorithms( cipher, mac, decompressor )
140
+ super( cipher, mac )
141
+ @decompressor = decompressor
142
+ end
143
+
144
+ # Retrieve the next packet from the string, after (possibly) decrypting
145
+ # and decompressing it. The packet is returned as a reader buffer.
146
+ def get
147
+ @mutex.synchronize do
148
+ # get the first block of data
149
+ if @log.debug?
150
+ @log.debug "reading #{@cipher.block_size} bytes from socket..."
151
+ end
152
+ data = @socket.recv( @cipher.block_size )
153
+
154
+ # if the data is empty, then the socket was closed
155
+ if data.length < 1
156
+ raise Net::SSH::Transport::Disconnect,
157
+ "connection closed by remote host"
158
+ end
159
+
160
+ # decipher it
161
+ reader = @buffers.reader( @cipher.update( data ) )
162
+
163
+ # determine the packet length and how many bytes remain to be read
164
+ packet_length = reader.read_long
165
+ remaining_to_read = packet_length + 4 - @cipher.block_size
166
+ if @log.debug?
167
+ @log.debug "packet length(#{packet_length}) " +
168
+ "remaining(#{remaining_to_read})"
169
+ end
170
+
171
+ # read the remainder of the packet and decrypt it.
172
+ data = ""
173
+ loop do
174
+ data << @socket.recv( remaining_to_read - data.length )
175
+ break if data.length >= remaining_to_read
176
+ end
177
+
178
+ reader.append @cipher.update( data )
179
+ reader.append @cipher.final
180
+
181
+ padding_length = reader.read_byte
182
+
183
+ payload = reader.read( packet_length - padding_length - 1 )
184
+ padding = reader.read( padding_length ) if padding_length > 0
185
+
186
+ # get the hmac from the tail of the packet (if one exists), and
187
+ # then validate it.
188
+ hmac = @hmac.mac_length > 0 ? @socket.recv( @hmac.mac_length ) : ""
189
+
190
+ my_computed_hmac = compute_hmac( reader.content )
191
+ raise Net::SSH::Exception,
192
+ "corrupted mac detected" if hmac != my_computed_hmac
193
+
194
+ # decompress the payload
195
+ payload = @decompressor.decompress( payload )
196
+
197
+ increment_sequence_number
198
+
199
+ buffer = @buffers.reader( payload )
200
+ @log.debug "received: #{buffer.content.inspect}" if @log.debug?
201
+
202
+ return buffer
203
+ end
204
+ end
205
+
206
+ end
207
+
208
+ end
209
+ end
210
+ end
@@ -0,0 +1,146 @@
1
+ #--
2
+ # =============================================================================
3
+ # Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
4
+ # All rights reserved.
5
+ #
6
+ # This source file is distributed as part of the Net::SSH Secure Shell Client
7
+ # library for Ruby. This file (and the library as a whole) may be used only as
8
+ # allowed by either the BSD license, or the Ruby license (or, by association
9
+ # with the Ruby license, the GPL). See the "doc" subdirectory of the Net::SSH
10
+ # distribution for the texts of these licenses.
11
+ # -----------------------------------------------------------------------------
12
+ # net-ssh website : http://net-ssh.rubyforge.org
13
+ # project website: http://rubyforge.org/projects/net-ssh
14
+ # =============================================================================
15
+ #++
16
+
17
+ module Net
18
+ module SSH
19
+ module Transport
20
+
21
+ # Register the services that together implement the SSH transport layer.
22
+ def register_services( container )
23
+ container.namespace_define :transport do |b|
24
+ b.kex_names { Hash.new }
25
+ b.compression_algorithms { Hash.new }
26
+ b.decompression_algorithms { Hash.new }
27
+
28
+ b.cipher_factories { Hash.new }
29
+ b.hmac_factories { Hash.new }
30
+ b.key_factories { Hash.new }
31
+ b.buffer_factories { Hash.new }
32
+ b.bn_factories { Hash.new }
33
+ b.digest_factories { Hash.new }
34
+
35
+ b.ciphers( :model => :prototype ) { |c,|
36
+ c.cipher_factories.fetch( c.crypto_backend ) }
37
+
38
+ b.hmacs( :model => :prototype ) { |c,|
39
+ c.hmac_factories.fetch( c.crypto_backend ) }
40
+
41
+ b.keys( :model => :prototype ) { |c,|
42
+ c.key_factories.fetch( c.crypto_backend ) }
43
+
44
+ b.buffers( :model => :prototype ) { |c,|
45
+ c.buffer_factories.fetch( c.crypto_backend ) }
46
+
47
+ b.bns( :model => :prototype ) { |c,|
48
+ c.bn_factories.fetch( c.crypto_backend ) }
49
+
50
+ b.digesters( :model => :prototype ) { |c,|
51
+ c.digest_factories.fetch( c.crypto_backend ) }
52
+
53
+ b.identity_cipher do
54
+ require 'net/ssh/transport/identity-cipher'
55
+ IdentityCipher.new
56
+ end
57
+
58
+ b.outgoing_packet_stream :model => :prototype_deferred do |c,|
59
+ require 'net/ssh/transport/packet-stream'
60
+ OutgoingPacketStream.new(
61
+ c.ciphers, c.hmacs, c.compression_algorithms )
62
+ end
63
+
64
+ b.incoming_packet_stream :model => :prototype_deferred do |c,point|
65
+ require 'net/ssh/transport/packet-stream'
66
+ stream = IncomingPacketStream.new(
67
+ c.ciphers, c.hmacs, c.decompression_algorithms )
68
+ stream.buffers = c.buffers
69
+ stream.log = c.log_for( point )
70
+ stream
71
+ end
72
+
73
+ b.algorithms do
74
+ Hash[
75
+ :host_key => [ "ssh-dss", "ssh-rsa" ],
76
+ :kex => [ "diffie-hellman-group-exchange-sha1",
77
+ "diffie-hellman-group1-sha1" ],
78
+ :encryption => [ "3des-cbc",
79
+ "aes128-cbc",
80
+ "blowfish-cbc",
81
+ "aes256-cbc",
82
+ "aes192-cbc",
83
+ "idea-cbc",
84
+ "none" ],
85
+ :hmac => [ "hmac-md5",
86
+ "hmac-sha1",
87
+ "hmac-md5-96",
88
+ "hmac-sha1-96",
89
+ "none" ],
90
+ :compression => [ "none", "zlib" ],
91
+ :languages => []
92
+ ]
93
+ end
94
+
95
+ b.default_ssh_port { 22 }
96
+
97
+ b.socket_factory do
98
+ require 'socket'
99
+ TCPSocket
100
+ end
101
+
102
+ b.version_negotiator do |c,point|
103
+ require 'net/ssh/transport/version-negotiator'
104
+ VersionNegotiator.new( c.log_for( point ) )
105
+ end
106
+
107
+ b.algorithm_negotiator do |c,point|
108
+ require 'net/ssh/transport/algorithm-negotiator'
109
+ AlgorithmNegotiator.new(
110
+ c.log_for( point ),
111
+ c.algorithms,
112
+ c.buffers )
113
+ end
114
+
115
+ b.session do |c,point|
116
+ require 'net/ssh/transport/session'
117
+
118
+ args = [ c[:transport_host] ]
119
+ args << c[:transport_options] if c.knows_key?(:transport_options)
120
+
121
+ Session.new( *args ) do |s|
122
+ s.logger = c[:log_for, point]
123
+ s.default_port = c[:default_ssh_port]
124
+ s.version_negotiator = c[:version_negotiator]
125
+ s.algorithm_negotiator = c[:algorithm_negotiator]
126
+ s.socket_factory = c[:socket_factory]
127
+ s.packet_sender = c[:outgoing_packet_stream]
128
+ s.packet_receiver = c[:incoming_packet_stream]
129
+ s.ciphers = c[:ciphers]
130
+ s.hmacs = c[:hmacs]
131
+ s.kexs = c[:kex_names]
132
+ s.compressors = c[:compression_algorithms]
133
+ s.decompressors = c[:decompression_algorithms]
134
+ end
135
+ end
136
+
137
+ b.require 'net/ssh/transport/ossl/services', "#{self}::OSSL"
138
+ b.require 'net/ssh/transport/compress/services', "#{self}::Compress"
139
+ b.require 'net/ssh/transport/kex/services', "#{self}::Kex"
140
+ end
141
+ end
142
+ module_function :register_services
143
+
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,296 @@
1
+ #--
2
+ # =============================================================================
3
+ # Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
4
+ # All rights reserved.
5
+ #
6
+ # This source file is distributed as part of the Net::SSH Secure Shell Client
7
+ # library for Ruby. This file (and the library as a whole) may be used only as
8
+ # allowed by either the BSD license, or the Ruby license (or, by association
9
+ # with the Ruby license, the GPL). See the "doc" subdirectory of the Net::SSH
10
+ # distribution for the texts of these licenses.
11
+ # -----------------------------------------------------------------------------
12
+ # net-ssh website : http://net-ssh.rubyforge.org
13
+ # project website: http://rubyforge.org/projects/net-ssh
14
+ # =============================================================================
15
+ #++
16
+
17
+ require 'socket'
18
+ require 'net/ssh/transport/constants'
19
+ require 'net/ssh/transport/errors'
20
+ require 'net/ssh/version'
21
+
22
+ module Net
23
+ module SSH
24
+ module Transport
25
+
26
+ # Represents a low-level SSH session, at the transport protocol level.
27
+ # This handles the algorithm negotiation and key exchange for any SSH
28
+ # connection.
29
+ class Session
30
+ include Constants
31
+
32
+ # the unique session identifier
33
+ attr_reader :session_id
34
+
35
+ # the collection of algorithms currently being used
36
+ attr_reader :algorithms
37
+
38
+ attr_writer :logger
39
+ attr_writer :default_port
40
+ attr_writer :version_negotiator
41
+ attr_writer :algorithm_negotiator
42
+ attr_writer :socket_factory
43
+ attr_writer :packet_sender
44
+ attr_writer :packet_receiver
45
+ attr_writer :ciphers
46
+ attr_writer :hmacs
47
+ attr_writer :kexs
48
+ attr_writer :compressors
49
+ attr_writer :decompressors
50
+
51
+ # The name that Net::SSH reports for itself
52
+ NAME = "Ruby/Net::SSH"
53
+
54
+ # The SSH protocol supported by Net::SSH.
55
+ PROTOCOL = "SSH-2.0"
56
+
57
+ # Returns the version string of this client.
58
+ def self.version
59
+ "#{PROTOCOL}-#{NAME}_#{Net::SSH::Version::STRING}"
60
+ end
61
+
62
+ VALID_OPTIONS = [ :port, :host_key, :kex, :encryption, :hmac,
63
+ :compression, :languages, :compression_level, :proxy ]
64
+
65
+ # Create a new connection to the given host. This will negotiate the
66
+ # algorithms to use and exchange the keys. A block must be given. The
67
+ # uninitialized +self+ will be passed to the block, so that dependencies
68
+ # may be injected.
69
+ def initialize( host, options={} )
70
+ @saved_message = nil
71
+ @session_id = nil
72
+
73
+ yield self
74
+
75
+ invalid_options = options.keys - VALID_OPTIONS
76
+
77
+ unless invalid_options.empty?
78
+ raise ArgumentError,
79
+ "invalid option(s) to #{self.class}: #{invalid_options.inspect}"
80
+ end
81
+
82
+ @port = options[ :port ] || @default_port
83
+ @socket = ( options[:proxy] || @socket_factory ).open( host, @port )
84
+
85
+ @packet_sender.socket = @socket
86
+ @packet_receiver.socket = @socket
87
+
88
+ @kex_info = {
89
+ :client_version_string => self.class.version,
90
+ :server_version_string =>
91
+ @version_negotiator.negotiate( @socket, self.class.version ) }
92
+
93
+ @options = options
94
+ kexinit
95
+ end
96
+
97
+ # Returns the name of the client's host, as reported by the socket.
98
+ def client_name
99
+ return @hostname if defined? @hostname
100
+
101
+ sockaddr = @socket.getsockname
102
+ begin
103
+ @hostname =
104
+ Socket.getnameinfo( sockaddr, Socket::NI_NAMEREQD ).first
105
+ rescue
106
+ @hostname = Socket.getnameinfo( sockaddr ).first
107
+ end
108
+
109
+ return @hostname
110
+ end
111
+
112
+ def kexinit
113
+ @doing_kexinit = true
114
+ @algorithms = @algorithm_negotiator.negotiate( self, @options )
115
+
116
+ @kex_info[ :server_algorithm_packet ] = @algorithms.server_packet
117
+ @kex_info[ :client_algorithm_packet ] = @algorithms.client_packet
118
+
119
+ exchange_keys
120
+ @doing_kexinit = false
121
+ end
122
+ private :kexinit
123
+
124
+ # Closes the connection.
125
+ def close
126
+ # TODO: send a DISCONNECT message to the server to close gracefully
127
+ @socket.shutdown
128
+ end
129
+
130
+ def get_kex_byte_requirement
131
+ need = 0
132
+
133
+ [ @algorithms.encryption_s2c,
134
+ @algorithms.encryption_c2s
135
+ ].each do |alg|
136
+ key_len, block_size = @ciphers.get_lengths( alg )
137
+ need = key_len if need < key_len
138
+ need = block_size if need < block_size
139
+ end
140
+
141
+ [ @algorithms.mac_c2s, @algorithms.mac_s2c ].each do |alg|
142
+ key_len = @hmacs.get_key_length( alg )
143
+ need = key_len if need < key_len
144
+ end
145
+
146
+ return need
147
+ end
148
+ private :get_kex_byte_requirement
149
+
150
+ # Exchanges keys with the server, using the kex algorithm negotiated
151
+ # during the algorithm negotiation phase. After finishing this phase,
152
+ # further packets sent to or from the server will be encrypted and
153
+ # (possibly) compressed.
154
+ def exchange_keys
155
+ @kex_info[ :need_bytes ] = get_kex_byte_requirement
156
+
157
+ kex = @kexs.fetch( @algorithms.kex )
158
+ result = kex.exchange_keys( self, @kex_info )
159
+
160
+ @shared_secret = result[ :shared_secret ]
161
+ hash = result[ :session_id ]
162
+ @session_id = hash unless @session_id
163
+ @server_key = result[ :server_key ]
164
+ @hashing_algorithm = result[ :hashing_algorithm ]
165
+
166
+ # prepare the ciphers, et. al.
167
+ secret_bin = @shared_secret.to_ssh
168
+
169
+ iv_c2s = @hashing_algorithm.digest( secret_bin +
170
+ hash +
171
+ "A" +
172
+ @session_id )
173
+ iv_s2c = @hashing_algorithm.digest( secret_bin +
174
+ hash +
175
+ "B" +
176
+ @session_id )
177
+ key_c2s = @hashing_algorithm.digest( secret_bin +
178
+ hash +
179
+ "C" +
180
+ @session_id )
181
+ key_s2c = @hashing_algorithm.digest( secret_bin +
182
+ hash +
183
+ "D" +
184
+ @session_id )
185
+ mac_key_c2s = @hashing_algorithm.digest( secret_bin +
186
+ hash +
187
+ "E" +
188
+ @session_id )
189
+ mac_key_s2c = @hashing_algorithm.digest( secret_bin +
190
+ hash +
191
+ "F" +
192
+ @session_id )
193
+
194
+ cipher_c2s = @ciphers.get(
195
+ @algorithms.encryption_c2s, iv_c2s, key_c2s,
196
+ secret_bin, hash, @hashing_algorithm,
197
+ true )
198
+
199
+ cipher_s2c = @ciphers.get(
200
+ @algorithms.encryption_s2c, iv_s2c, key_s2c,
201
+ secret_bin, hash, @hashing_algorithm,
202
+ false )
203
+
204
+ mac_c2s = @hmacs.get( @algorithms.mac_c2s, mac_key_c2s );
205
+ mac_s2c = @hmacs.get( @algorithms.mac_s2c, mac_key_s2c );
206
+
207
+ compression_c2s = @compressors[ @algorithms.compression_c2s ].new(
208
+ :level => @algorithms.compression_level )
209
+ compression_s2c = @decompressors[ @algorithms.compression_s2c ].new
210
+
211
+ @packet_sender.set_algorithms cipher_c2s, mac_c2s, compression_c2s
212
+ @packet_receiver.set_algorithms cipher_s2c, mac_s2c, compression_s2c
213
+ end
214
+ private :exchange_keys
215
+
216
+ # Waits for the next message from the server, handling common requests
217
+ # like DISCONNECT, IGNORE, DEBUG, and KEXINIT in the background. The
218
+ # next message is returned as a [ type, buffer ] tuple, where the buffer
219
+ # is a Net::SSH::Util::ReaderBuffer.
220
+ def wait_for_message
221
+ buffer = type = nil
222
+
223
+ if @saved_message
224
+ type, buffer = @saved_message
225
+ @logger.debug "returning saved message: #{type}" if @logger.debug?
226
+ @saved_message = nil
227
+ else
228
+ loop do
229
+ if @logger.debug?
230
+ @logger.debug "waiting for packet from server..."
231
+ end
232
+
233
+ buffer = @packet_receiver.get
234
+ type = buffer.read_byte
235
+ @logger.debug "got packet of type #{type}" if @logger.debug?
236
+
237
+ case type
238
+ when DISCONNECT
239
+ reason_code = buffer.read_long
240
+ description = buffer.read_string
241
+ language = buffer.read_string
242
+ raise Net::SSH::Transport::Disconnect,
243
+ "disconnected: #{description} (#{reason_code})"
244
+
245
+ when IGNORE
246
+ # do nothing
247
+ @logger.info "received IGNORE message " +
248
+ "(#{buffer.read_string.inspect})" if @logger.debug?
249
+
250
+ when DEBUG
251
+ # do nothing
252
+ @logger.info "received DEBUG message" if @logger.debug?
253
+ always_display = buffer.read_bool
254
+ message = buffer.read_string
255
+ language = buffer.read_string
256
+ if always_display
257
+ @logger.warn "#{message} (#{language})" if @logger.warn?
258
+ else
259
+ @logger.debug "#{message} (#{language})" if @logger.debug?
260
+ end
261
+
262
+ when KEXINIT
263
+ # unless we're already doing a key-exchange, do key
264
+ # re-exchange
265
+ if !@doing_kexinit
266
+ @logger.info "re-key requested" if @logger.info?
267
+ @saved_message = [ type, buffer ]
268
+ kexinit
269
+ else
270
+ break
271
+ end
272
+
273
+ else
274
+ break
275
+ end
276
+ end
277
+ end
278
+
279
+ return type, buffer
280
+ end
281
+
282
+ # Sends the given payload, using the currently configured
283
+ # OutgoingPacketStream.
284
+ def send_message( message )
285
+ if @logger.debug?
286
+ @logger.debug "sending message >>#{message.to_s.inspect}<<"
287
+ end
288
+
289
+ @packet_sender.send message
290
+ end
291
+
292
+ end
293
+
294
+ end
295
+ end
296
+ end