jayniz-net-ssh 2.0.15

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 (108) hide show
  1. data/CHANGELOG.rdoc +161 -0
  2. data/Manifest +107 -0
  3. data/README.rdoc +140 -0
  4. data/Rakefile +79 -0
  5. data/Rudyfile +110 -0
  6. data/THANKS.rdoc +16 -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 +150 -0
  19. data/lib/net/ssh/config.rb +185 -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 +597 -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 +142 -0
  34. data/lib/net/ssh/ruby_compat.rb +43 -0
  35. data/lib/net/ssh/service/forward.rb +267 -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/test.rb +89 -0
  45. data/lib/net/ssh/transport/algorithms.rb +384 -0
  46. data/lib/net/ssh/transport/cipher_factory.rb +97 -0
  47. data/lib/net/ssh/transport/constants.rb +30 -0
  48. data/lib/net/ssh/transport/hmac/abstract.rb +79 -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/hmac.rb +31 -0
  55. data/lib/net/ssh/transport/identity_cipher.rb +55 -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/kex.rb +13 -0
  59. data/lib/net/ssh/transport/openssl.rb +128 -0
  60. data/lib/net/ssh/transport/packet_stream.rb +232 -0
  61. data/lib/net/ssh/transport/server_version.rb +70 -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/lib/net/ssh.rb +215 -0
  69. data/net-ssh.gemspec +131 -0
  70. data/setup.rb +1585 -0
  71. data/support/arcfour_check.rb +20 -0
  72. data/test/authentication/methods/common.rb +28 -0
  73. data/test/authentication/methods/test_abstract.rb +51 -0
  74. data/test/authentication/methods/test_hostbased.rb +114 -0
  75. data/test/authentication/methods/test_keyboard_interactive.rb +98 -0
  76. data/test/authentication/methods/test_password.rb +50 -0
  77. data/test/authentication/methods/test_publickey.rb +127 -0
  78. data/test/authentication/test_agent.rb +205 -0
  79. data/test/authentication/test_key_manager.rb +105 -0
  80. data/test/authentication/test_session.rb +93 -0
  81. data/test/common.rb +107 -0
  82. data/test/configs/eqsign +3 -0
  83. data/test/configs/exact_match +8 -0
  84. data/test/configs/multihost +4 -0
  85. data/test/configs/wild_cards +14 -0
  86. data/test/connection/test_channel.rb +452 -0
  87. data/test/connection/test_session.rb +488 -0
  88. data/test/test_all.rb +8 -0
  89. data/test/test_buffer.rb +336 -0
  90. data/test/test_buffered_io.rb +63 -0
  91. data/test/test_config.rb +99 -0
  92. data/test/test_key_factory.rb +67 -0
  93. data/test/transport/hmac/test_md5.rb +39 -0
  94. data/test/transport/hmac/test_md5_96.rb +25 -0
  95. data/test/transport/hmac/test_none.rb +34 -0
  96. data/test/transport/hmac/test_sha1.rb +34 -0
  97. data/test/transport/hmac/test_sha1_96.rb +25 -0
  98. data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +146 -0
  99. data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +92 -0
  100. data/test/transport/test_algorithms.rb +302 -0
  101. data/test/transport/test_cipher_factory.rb +213 -0
  102. data/test/transport/test_hmac.rb +34 -0
  103. data/test/transport/test_identity_cipher.rb +40 -0
  104. data/test/transport/test_packet_stream.rb +441 -0
  105. data/test/transport/test_server_version.rb +68 -0
  106. data/test/transport/test_session.rb +315 -0
  107. data/test/transport/test_state.rb +173 -0
  108. metadata +168 -0
@@ -0,0 +1,77 @@
1
+ require 'net/ssh/errors'
2
+ require 'net/ssh/transport/constants'
3
+ require 'net/ssh/transport/kex/diffie_hellman_group1_sha1'
4
+
5
+ module Net::SSH::Transport::Kex
6
+
7
+ # A key-exchange service implementing the
8
+ # "diffie-hellman-group-exchange-sha1" key-exchange algorithm.
9
+ class DiffieHellmanGroupExchangeSHA1 < DiffieHellmanGroup1SHA1
10
+ MINIMUM_BITS = 1024
11
+ MAXIMUM_BITS = 8192
12
+
13
+ KEXDH_GEX_GROUP = 31
14
+ KEXDH_GEX_INIT = 32
15
+ KEXDH_GEX_REPLY = 33
16
+ KEXDH_GEX_REQUEST = 34
17
+
18
+ private
19
+
20
+ # Compute the number of bits needed for the given number of bytes.
21
+ def compute_need_bits
22
+ need_bits = data[:need_bytes] * 8
23
+ if need_bits < MINIMUM_BITS
24
+ need_bits = MINIMUM_BITS
25
+ elsif need_bits > MAXIMUM_BITS
26
+ need_bits = MAXIMUM_BITS
27
+ end
28
+
29
+ data[:need_bits ] = need_bits
30
+ data[:need_bytes] = need_bits / 8
31
+ end
32
+
33
+ # Returns the DH key parameters for the given session.
34
+ def get_parameters
35
+ compute_need_bits
36
+
37
+ # request the DH key parameters for the given number of bits.
38
+ buffer = Net::SSH::Buffer.from(:byte, KEXDH_GEX_REQUEST, :long, MINIMUM_BITS,
39
+ :long, data[:need_bits], :long, MAXIMUM_BITS)
40
+ connection.send_message(buffer)
41
+
42
+ buffer = connection.next_message
43
+ unless buffer.type == KEXDH_GEX_GROUP
44
+ raise Net::SSH::Exception, "expected KEXDH_GEX_GROUP, got #{buffer.type}"
45
+ end
46
+
47
+ p = buffer.read_bignum
48
+ g = buffer.read_bignum
49
+
50
+ [p, g]
51
+ end
52
+
53
+ # Returns the INIT/REPLY constants used by this algorithm.
54
+ def get_message_types
55
+ [KEXDH_GEX_INIT, KEXDH_GEX_REPLY]
56
+ end
57
+
58
+ # Build the signature buffer to use when verifying a signature from
59
+ # the server.
60
+ def build_signature_buffer(result)
61
+ response = Net::SSH::Buffer.new
62
+ response.write_string data[:client_version_string],
63
+ data[:server_version_string],
64
+ data[:client_algorithm_packet],
65
+ data[:server_algorithm_packet],
66
+ result[:key_blob]
67
+ response.write_long MINIMUM_BITS,
68
+ data[:need_bits],
69
+ MAXIMUM_BITS
70
+ response.write_bignum dh.p, dh.g, dh.pub_key,
71
+ result[:server_dh_pubkey],
72
+ result[:shared_secret]
73
+ response
74
+ end
75
+ end
76
+
77
+ end
@@ -0,0 +1,13 @@
1
+ require 'net/ssh/transport/kex/diffie_hellman_group1_sha1'
2
+ require 'net/ssh/transport/kex/diffie_hellman_group_exchange_sha1'
3
+
4
+ module Net::SSH::Transport
5
+ module Kex
6
+ # Maps the supported key-exchange algorithms as named by the SSH protocol
7
+ # to their corresponding implementors.
8
+ MAP = {
9
+ 'diffie-hellman-group-exchange-sha1' => DiffieHellmanGroupExchangeSHA1,
10
+ 'diffie-hellman-group1-sha1' => DiffieHellmanGroup1SHA1
11
+ }
12
+ end
13
+ end
@@ -0,0 +1,128 @@
1
+ require 'openssl'
2
+ require 'net/ssh/buffer'
3
+
4
+ module OpenSSL
5
+
6
+ # This class is originally defined in the OpenSSL module. As needed, methods
7
+ # have been added to it by the Net::SSH module for convenience in dealing with
8
+ # SSH functionality.
9
+ class BN
10
+
11
+ # Converts a BN object to a string. The format used is that which is
12
+ # required by the SSH2 protocol.
13
+ def to_ssh
14
+ if zero?
15
+ return [0].pack("N")
16
+ else
17
+ buf = to_s(2)
18
+ if buf.getbyte(0)[7] == 1
19
+ return [buf.length+1, 0, buf].pack("NCA*")
20
+ else
21
+ return [buf.length, buf].pack("NA*")
22
+ end
23
+ end
24
+ end
25
+
26
+ end
27
+
28
+ module PKey
29
+
30
+ class PKey
31
+ def fingerprint
32
+ @fingerprint ||= OpenSSL::Digest::MD5.hexdigest(to_blob).scan(/../).join(":")
33
+ end
34
+ end
35
+
36
+ # This class is originally defined in the OpenSSL module. As needed, methods
37
+ # have been added to it by the Net::SSH module for convenience in dealing
38
+ # with SSH functionality.
39
+ class DH
40
+
41
+ # Determines whether the pub_key for this key is valid. (This algorithm
42
+ # lifted more-or-less directly from OpenSSH, dh.c, dh_pub_is_valid.)
43
+ def valid?
44
+ return false if pub_key.nil? || pub_key < 0
45
+ bits_set = 0
46
+ pub_key.num_bits.times { |i| bits_set += 1 if pub_key.bit_set?(i) }
47
+ return ( bits_set > 1 && pub_key < p )
48
+ end
49
+
50
+ end
51
+
52
+ # This class is originally defined in the OpenSSL module. As needed, methods
53
+ # have been added to it by the Net::SSH module for convenience in dealing
54
+ # with SSH functionality.
55
+ class RSA
56
+
57
+ # Returns "ssh-rsa", which is the description of this key type used by the
58
+ # SSH2 protocol.
59
+ def ssh_type
60
+ "ssh-rsa"
61
+ end
62
+
63
+ # Converts the key to a blob, according to the SSH2 protocol.
64
+ def to_blob
65
+ @blob ||= Net::SSH::Buffer.from(:string, ssh_type, :bignum, e, :bignum, n).to_s
66
+ end
67
+
68
+ # Verifies the given signature matches the given data.
69
+ def ssh_do_verify(sig, data)
70
+ verify(OpenSSL::Digest::SHA1.new, sig, data)
71
+ end
72
+
73
+ # Returns the signature for the given data.
74
+ def ssh_do_sign(data)
75
+ sign(OpenSSL::Digest::SHA1.new, data)
76
+ end
77
+ end
78
+
79
+ # This class is originally defined in the OpenSSL module. As needed, methods
80
+ # have been added to it by the Net::SSH module for convenience in dealing
81
+ # with SSH functionality.
82
+ class DSA
83
+
84
+ # Returns "ssh-dss", which is the description of this key type used by the
85
+ # SSH2 protocol.
86
+ def ssh_type
87
+ "ssh-dss"
88
+ end
89
+
90
+ # Converts the key to a blob, according to the SSH2 protocol.
91
+ def to_blob
92
+ @blob ||= Net::SSH::Buffer.from(:string, ssh_type,
93
+ :bignum, p, :bignum, q, :bignum, g, :bignum, pub_key).to_s
94
+ end
95
+
96
+ # Verifies the given signature matches the given data.
97
+ def ssh_do_verify(sig, data)
98
+ sig_r = sig[0,20].unpack("H*")[0].to_i(16)
99
+ sig_s = sig[20,20].unpack("H*")[0].to_i(16)
100
+ a1sig = OpenSSL::ASN1::Sequence([
101
+ OpenSSL::ASN1::Integer(sig_r),
102
+ OpenSSL::ASN1::Integer(sig_s)
103
+ ])
104
+ return verify(OpenSSL::Digest::DSS1.new, a1sig.to_der, data)
105
+ end
106
+
107
+ # Signs the given data.
108
+ def ssh_do_sign(data)
109
+ sig = sign( OpenSSL::Digest::DSS1.new, data)
110
+ a1sig = OpenSSL::ASN1.decode( sig )
111
+
112
+ sig_r = a1sig.value[0].value.to_s(2)
113
+ sig_s = a1sig.value[1].value.to_s(2)
114
+
115
+ if sig_r.length > 20 || sig_s.length > 20
116
+ raise OpenSSL::PKey::DSAError, "bad sig size"
117
+ end
118
+
119
+ sig_r = "\0" * ( 20 - sig_r.length ) + sig_r if sig_r.length < 20
120
+ sig_s = "\0" * ( 20 - sig_s.length ) + sig_s if sig_s.length < 20
121
+
122
+ return sig_r + sig_s
123
+ end
124
+ end
125
+
126
+ end
127
+
128
+ end
@@ -0,0 +1,232 @@
1
+ require 'net/ssh/buffered_io'
2
+ require 'net/ssh/errors'
3
+ require 'net/ssh/packet'
4
+ require 'net/ssh/ruby_compat'
5
+ require 'net/ssh/transport/cipher_factory'
6
+ require 'net/ssh/transport/hmac'
7
+ require 'net/ssh/transport/state'
8
+
9
+
10
+ module Net; module SSH; module Transport
11
+
12
+ # A module that builds additional functionality onto the Net::SSH::BufferedIo
13
+ # module. It adds SSH encryption, compression, and packet validation, as
14
+ # per the SSH2 protocol. It also adds an abstraction for polling packets,
15
+ # to allow for both blocking and non-blocking reads.
16
+ module PacketStream
17
+ include BufferedIo
18
+
19
+ def self.extended(object)
20
+ object.__send__(:initialize_ssh)
21
+ end
22
+
23
+ # The map of "hints" that can be used to modify the behavior of the packet
24
+ # stream. For instance, when authentication succeeds, an "authenticated"
25
+ # hint is set, which is used to determine whether or not to compress the
26
+ # data when using the "delayed" compression algorithm.
27
+ attr_reader :hints
28
+
29
+ # The server state object, which encapsulates the algorithms used to interpret
30
+ # packets coming from the server.
31
+ attr_reader :server
32
+
33
+ # The client state object, which encapsulates the algorithms used to build
34
+ # packets to send to the server.
35
+ attr_reader :client
36
+
37
+ # The name of the client (local) end of the socket, as reported by the
38
+ # socket.
39
+ def client_name
40
+ @client_name ||= begin
41
+ sockaddr = getsockname
42
+ begin
43
+ Socket.getnameinfo(sockaddr, Socket::NI_NAMEREQD).first
44
+ rescue
45
+ begin
46
+ Socket.getnameinfo(sockaddr).first
47
+ rescue
48
+ begin
49
+ Socket.gethostbyname(Socket.gethostname).first
50
+ rescue
51
+ lwarn { "the client ipaddr/name could not be determined" }
52
+ "unknown"
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ # The IP address of the peer (remote) end of the socket, as reported by
60
+ # the socket.
61
+ def peer_ip
62
+ @peer_ip ||= begin
63
+ addr = getpeername
64
+ Socket.getnameinfo(addr, Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV).first
65
+ end
66
+ end
67
+
68
+ # Returns true if the IO is available for reading, and false otherwise.
69
+ def available_for_read?
70
+ result = Net::SSH::Compat.io_select([self], nil, nil, 0)
71
+ result && result.first.any?
72
+ end
73
+
74
+ # Returns the next full packet. If the mode parameter is :nonblock (the
75
+ # default), then this will return immediately, whether a packet is
76
+ # available or not, and will return nil if there is no packet ready to be
77
+ # returned. If the mode parameter is :block, then this method will block
78
+ # until a packet is available.
79
+ def next_packet(mode=:nonblock)
80
+ case mode
81
+ when :nonblock then
82
+ fill if available_for_read?
83
+ poll_next_packet
84
+
85
+ when :block then
86
+ loop do
87
+ packet = poll_next_packet
88
+ return packet if packet
89
+
90
+ loop do
91
+ result = Net::SSH::Compat.io_select([self]) or next
92
+ break if result.first.any?
93
+ end
94
+
95
+ if fill <= 0
96
+ raise Net::SSH::Disconnect, "connection closed by remote host"
97
+ end
98
+ end
99
+
100
+ else
101
+ raise ArgumentError, "expected :block or :nonblock, got #{mode.inspect}"
102
+ end
103
+ end
104
+
105
+ # Enqueues a packet to be sent, and blocks until the entire packet is
106
+ # sent.
107
+ def send_packet(payload)
108
+ enqueue_packet(payload)
109
+ wait_for_pending_sends
110
+ end
111
+
112
+ # Enqueues a packet to be sent, but does not immediately send the packet.
113
+ # The given payload is pre-processed according to the algorithms specified
114
+ # in the client state (compression, cipher, and hmac).
115
+ def enqueue_packet(payload)
116
+ # try to compress the packet
117
+ payload = client.compress(payload)
118
+
119
+ # the length of the packet, minus the padding
120
+ actual_length = 4 + payload.length + 1
121
+
122
+ # compute the padding length
123
+ padding_length = client.block_size - (actual_length % client.block_size)
124
+ padding_length += client.block_size if padding_length < 4
125
+
126
+ # compute the packet length (sans the length field itself)
127
+ packet_length = payload.length + padding_length + 1
128
+
129
+ if packet_length < 16
130
+ padding_length += client.block_size
131
+ packet_length = payload.length + padding_length + 1
132
+ end
133
+
134
+ padding = Array.new(padding_length) { rand(256) }.pack("C*")
135
+
136
+ unencrypted_data = [packet_length, padding_length, payload, padding].pack("NCA*A*")
137
+ mac = client.hmac.digest([client.sequence_number, unencrypted_data].pack("NA*"))
138
+
139
+ encrypted_data = client.update_cipher(unencrypted_data) << client.final_cipher
140
+ message = encrypted_data + mac
141
+
142
+ debug { "queueing packet nr #{client.sequence_number} type #{payload.getbyte(0)} len #{packet_length}" }
143
+ enqueue(message)
144
+
145
+ client.increment(packet_length)
146
+
147
+ self
148
+ end
149
+
150
+ # Performs any pending cleanup necessary on the IO and its associated
151
+ # state objects. (See State#cleanup).
152
+ def cleanup
153
+ client.cleanup
154
+ server.cleanup
155
+ end
156
+
157
+ # If the IO object requires a rekey operation (as indicated by either its
158
+ # client or server state objects, see State#needs_rekey?), this will
159
+ # yield. Otherwise, this does nothing.
160
+ def if_needs_rekey?
161
+ if client.needs_rekey? || server.needs_rekey?
162
+ yield
163
+ client.reset! if client.needs_rekey?
164
+ server.reset! if server.needs_rekey?
165
+ end
166
+ end
167
+
168
+ protected
169
+
170
+ # Called when this module is used to extend an object. It initializes
171
+ # the states and generally prepares the object for use as a packet stream.
172
+ def initialize_ssh
173
+ @hints = {}
174
+ @server = State.new(self, :server)
175
+ @client = State.new(self, :client)
176
+ @packet = nil
177
+ initialize_buffered_io
178
+ end
179
+
180
+ # Tries to read the next packet. If there is insufficient data to read
181
+ # an entire packet, this returns immediately, otherwise the packet is
182
+ # read, post-processed according to the cipher, hmac, and compression
183
+ # algorithms specified in the server state object, and returned as a
184
+ # new Packet object.
185
+ def poll_next_packet
186
+ if @packet.nil?
187
+ minimum = server.block_size < 4 ? 4 : server.block_size
188
+ return nil if available < minimum
189
+ data = read_available(minimum)
190
+
191
+ # decipher it
192
+ @packet = Net::SSH::Buffer.new(server.update_cipher(data))
193
+ @packet_length = @packet.read_long
194
+ end
195
+
196
+ need = @packet_length + 4 - server.block_size
197
+ raise Net::SSH::Exception, "padding error, need #{need} block #{server.block_size}" if need % server.block_size != 0
198
+
199
+ return nil if available < need + server.hmac.mac_length
200
+
201
+ if need > 0
202
+ # read the remainder of the packet and decrypt it.
203
+ data = read_available(need)
204
+ @packet.append(server.update_cipher(data))
205
+ end
206
+
207
+ # get the hmac from the tail of the packet (if one exists), and
208
+ # then validate it.
209
+ real_hmac = read_available(server.hmac.mac_length) || ""
210
+
211
+ @packet.append(server.final_cipher)
212
+ padding_length = @packet.read_byte
213
+
214
+ payload = @packet.read(@packet_length - padding_length - 1)
215
+ padding = @packet.read(padding_length) if padding_length > 0
216
+
217
+ my_computed_hmac = server.hmac.digest([server.sequence_number, @packet.content].pack("NA*"))
218
+ raise Net::SSH::Exception, "corrupted mac detected" if real_hmac != my_computed_hmac
219
+
220
+ # try to decompress the payload, in case compression is active
221
+ payload = server.decompress(payload)
222
+
223
+ debug { "received packet nr #{server.sequence_number} type #{payload.getbyte(0)} len #{@packet_length}" }
224
+
225
+ server.increment(@packet_length)
226
+ @packet = nil
227
+
228
+ return Packet.new(payload)
229
+ end
230
+ end
231
+
232
+ end; end; end