minmb-net-ssh 2.5.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 (137) hide show
  1. data/CHANGELOG.rdoc +291 -0
  2. data/Manifest +132 -0
  3. data/README.rdoc +184 -0
  4. data/Rakefile +86 -0
  5. data/Rudyfile +96 -0
  6. data/THANKS.rdoc +19 -0
  7. data/lib/net/ssh.rb +223 -0
  8. data/lib/net/ssh/authentication/agent.rb +23 -0
  9. data/lib/net/ssh/authentication/agent/java_pageant.rb +85 -0
  10. data/lib/net/ssh/authentication/agent/socket.rb +170 -0
  11. data/lib/net/ssh/authentication/constants.rb +18 -0
  12. data/lib/net/ssh/authentication/key_manager.rb +253 -0
  13. data/lib/net/ssh/authentication/methods/abstract.rb +60 -0
  14. data/lib/net/ssh/authentication/methods/hostbased.rb +75 -0
  15. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +70 -0
  16. data/lib/net/ssh/authentication/methods/password.rb +43 -0
  17. data/lib/net/ssh/authentication/methods/publickey.rb +96 -0
  18. data/lib/net/ssh/authentication/pageant.rb +301 -0
  19. data/lib/net/ssh/authentication/session.rb +154 -0
  20. data/lib/net/ssh/buffer.rb +350 -0
  21. data/lib/net/ssh/buffered_io.rb +207 -0
  22. data/lib/net/ssh/config.rb +207 -0
  23. data/lib/net/ssh/connection/channel.rb +630 -0
  24. data/lib/net/ssh/connection/constants.rb +33 -0
  25. data/lib/net/ssh/connection/session.rb +603 -0
  26. data/lib/net/ssh/connection/term.rb +178 -0
  27. data/lib/net/ssh/errors.rb +88 -0
  28. data/lib/net/ssh/key_factory.rb +107 -0
  29. data/lib/net/ssh/known_hosts.rb +141 -0
  30. data/lib/net/ssh/loggable.rb +61 -0
  31. data/lib/net/ssh/packet.rb +102 -0
  32. data/lib/net/ssh/prompt.rb +93 -0
  33. data/lib/net/ssh/proxy/command.rb +75 -0
  34. data/lib/net/ssh/proxy/errors.rb +14 -0
  35. data/lib/net/ssh/proxy/http.rb +94 -0
  36. data/lib/net/ssh/proxy/socks4.rb +70 -0
  37. data/lib/net/ssh/proxy/socks5.rb +142 -0
  38. data/lib/net/ssh/ruby_compat.rb +77 -0
  39. data/lib/net/ssh/service/forward.rb +327 -0
  40. data/lib/net/ssh/test.rb +89 -0
  41. data/lib/net/ssh/test/channel.rb +129 -0
  42. data/lib/net/ssh/test/extensions.rb +152 -0
  43. data/lib/net/ssh/test/kex.rb +44 -0
  44. data/lib/net/ssh/test/local_packet.rb +51 -0
  45. data/lib/net/ssh/test/packet.rb +81 -0
  46. data/lib/net/ssh/test/remote_packet.rb +38 -0
  47. data/lib/net/ssh/test/script.rb +157 -0
  48. data/lib/net/ssh/test/socket.rb +64 -0
  49. data/lib/net/ssh/transport/algorithms.rb +407 -0
  50. data/lib/net/ssh/transport/cipher_factory.rb +106 -0
  51. data/lib/net/ssh/transport/constants.rb +32 -0
  52. data/lib/net/ssh/transport/ctr.rb +95 -0
  53. data/lib/net/ssh/transport/hmac.rb +45 -0
  54. data/lib/net/ssh/transport/hmac/abstract.rb +79 -0
  55. data/lib/net/ssh/transport/hmac/md5.rb +12 -0
  56. data/lib/net/ssh/transport/hmac/md5_96.rb +11 -0
  57. data/lib/net/ssh/transport/hmac/none.rb +15 -0
  58. data/lib/net/ssh/transport/hmac/ripemd160.rb +13 -0
  59. data/lib/net/ssh/transport/hmac/sha1.rb +13 -0
  60. data/lib/net/ssh/transport/hmac/sha1_96.rb +11 -0
  61. data/lib/net/ssh/transport/hmac/sha2_256.rb +15 -0
  62. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +13 -0
  63. data/lib/net/ssh/transport/hmac/sha2_512.rb +14 -0
  64. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +13 -0
  65. data/lib/net/ssh/transport/identity_cipher.rb +55 -0
  66. data/lib/net/ssh/transport/kex.rb +28 -0
  67. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +44 -0
  68. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +216 -0
  69. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +80 -0
  70. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +15 -0
  71. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +93 -0
  72. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +13 -0
  73. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +13 -0
  74. data/lib/net/ssh/transport/key_expander.rb +26 -0
  75. data/lib/net/ssh/transport/openssl.rb +237 -0
  76. data/lib/net/ssh/transport/packet_stream.rb +235 -0
  77. data/lib/net/ssh/transport/server_version.rb +71 -0
  78. data/lib/net/ssh/transport/session.rb +278 -0
  79. data/lib/net/ssh/transport/state.rb +206 -0
  80. data/lib/net/ssh/verifiers/lenient.rb +30 -0
  81. data/lib/net/ssh/verifiers/null.rb +12 -0
  82. data/lib/net/ssh/verifiers/strict.rb +53 -0
  83. data/lib/net/ssh/version.rb +62 -0
  84. data/net-ssh.gemspec +164 -0
  85. data/setup.rb +1585 -0
  86. data/support/arcfour_check.rb +20 -0
  87. data/support/ssh_tunnel_bug.rb +65 -0
  88. data/test/authentication/methods/common.rb +28 -0
  89. data/test/authentication/methods/test_abstract.rb +51 -0
  90. data/test/authentication/methods/test_hostbased.rb +114 -0
  91. data/test/authentication/methods/test_keyboard_interactive.rb +100 -0
  92. data/test/authentication/methods/test_password.rb +52 -0
  93. data/test/authentication/methods/test_publickey.rb +148 -0
  94. data/test/authentication/test_agent.rb +205 -0
  95. data/test/authentication/test_key_manager.rb +218 -0
  96. data/test/authentication/test_session.rb +106 -0
  97. data/test/common.rb +107 -0
  98. data/test/configs/eqsign +3 -0
  99. data/test/configs/exact_match +8 -0
  100. data/test/configs/host_plus +10 -0
  101. data/test/configs/multihost +4 -0
  102. data/test/configs/wild_cards +14 -0
  103. data/test/connection/test_channel.rb +467 -0
  104. data/test/connection/test_session.rb +488 -0
  105. data/test/known_hosts/github +1 -0
  106. data/test/test_all.rb +9 -0
  107. data/test/test_buffer.rb +426 -0
  108. data/test/test_buffered_io.rb +63 -0
  109. data/test/test_config.rb +120 -0
  110. data/test/test_key_factory.rb +121 -0
  111. data/test/test_known_hosts.rb +13 -0
  112. data/test/transport/hmac/test_md5.rb +39 -0
  113. data/test/transport/hmac/test_md5_96.rb +25 -0
  114. data/test/transport/hmac/test_none.rb +34 -0
  115. data/test/transport/hmac/test_ripemd160.rb +34 -0
  116. data/test/transport/hmac/test_sha1.rb +34 -0
  117. data/test/transport/hmac/test_sha1_96.rb +25 -0
  118. data/test/transport/hmac/test_sha2_256.rb +35 -0
  119. data/test/transport/hmac/test_sha2_256_96.rb +25 -0
  120. data/test/transport/hmac/test_sha2_512.rb +35 -0
  121. data/test/transport/hmac/test_sha2_512_96.rb +25 -0
  122. data/test/transport/kex/test_diffie_hellman_group14_sha1.rb +13 -0
  123. data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +146 -0
  124. data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +92 -0
  125. data/test/transport/kex/test_diffie_hellman_group_exchange_sha256.rb +33 -0
  126. data/test/transport/kex/test_ecdh_sha2_nistp256.rb +161 -0
  127. data/test/transport/kex/test_ecdh_sha2_nistp384.rb +37 -0
  128. data/test/transport/kex/test_ecdh_sha2_nistp521.rb +37 -0
  129. data/test/transport/test_algorithms.rb +330 -0
  130. data/test/transport/test_cipher_factory.rb +441 -0
  131. data/test/transport/test_hmac.rb +34 -0
  132. data/test/transport/test_identity_cipher.rb +40 -0
  133. data/test/transport/test_packet_stream.rb +1745 -0
  134. data/test/transport/test_server_version.rb +78 -0
  135. data/test/transport/test_session.rb +315 -0
  136. data/test/transport/test_state.rb +179 -0
  137. metadata +208 -0
@@ -0,0 +1,15 @@
1
+ require 'net/ssh/transport/kex/diffie_hellman_group_exchange_sha1'
2
+
3
+ module Net::SSH::Transport::Kex
4
+ if defined?(OpenSSL::Digest::SHA256)
5
+ # A key-exchange service implementing the
6
+ # "diffie-hellman-group-exchange-sha256" key-exchange algorithm.
7
+ class DiffieHellmanGroupExchangeSHA256 < DiffieHellmanGroupExchangeSHA1
8
+ def initialize(*args)
9
+ super(*args)
10
+
11
+ @digester = OpenSSL::Digest::SHA256
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,93 @@
1
+ require 'net/ssh/transport/constants'
2
+ require 'net/ssh/transport/kex/diffie_hellman_group1_sha1'
3
+
4
+ module Net; module SSH; module Transport; module Kex
5
+
6
+ # A key-exchange service implementing the "ecdh-sha2-nistp256"
7
+ # key-exchange algorithm. (defined in RFC 5656)
8
+ class EcdhSHA2NistP256 < DiffieHellmanGroup1SHA1
9
+ include Constants, Loggable
10
+
11
+ attr_reader :ecdh
12
+
13
+ def digester
14
+ OpenSSL::Digest::SHA256
15
+ end
16
+
17
+ def curve_name
18
+ OpenSSL::PKey::EC::CurveNameAlias['nistp256']
19
+ end
20
+
21
+ def initialize(algorithms, connection, data)
22
+ @algorithms = algorithms
23
+ @connection = connection
24
+
25
+ @digester = digester
26
+ @data = data.dup
27
+ @ecdh = generate_key
28
+ @logger = @data.delete(:logger)
29
+ end
30
+
31
+ private
32
+
33
+ def get_message_types
34
+ [KEXECDH_INIT, KEXECDH_REPLY]
35
+ end
36
+
37
+ def build_signature_buffer(result)
38
+ response = Net::SSH::Buffer.new
39
+ response.write_string data[:client_version_string],
40
+ data[:server_version_string],
41
+ data[:client_algorithm_packet],
42
+ data[:server_algorithm_packet],
43
+ result[:key_blob],
44
+ ecdh.public_key.to_bn.to_s(2),
45
+ result[:server_ecdh_pubkey]
46
+ response.write_bignum result[:shared_secret]
47
+ response
48
+ end
49
+
50
+ def generate_key #:nodoc:
51
+ OpenSSL::PKey::EC.new(curve_name).generate_key
52
+ end
53
+
54
+ def send_kexinit #:nodoc:
55
+ init, reply = get_message_types
56
+
57
+ # send the KEXECDH_INIT message
58
+ ## byte SSH_MSG_KEX_ECDH_INIT
59
+ ## string Q_C, client's ephemeral public key octet string
60
+ buffer = Net::SSH::Buffer.from(:byte, init, :string, ecdh.public_key.to_bn.to_s(2))
61
+ connection.send_message(buffer)
62
+
63
+ # expect the following KEXECDH_REPLY message
64
+ ## byte SSH_MSG_KEX_ECDH_REPLY
65
+ ## string K_S, server's public host key
66
+ ## string Q_S, server's ephemeral public key octet string
67
+ ## string the signature on the exchange hash
68
+ buffer = connection.next_message
69
+ raise Net::SSH::Exception, "expected REPLY" unless buffer.type == reply
70
+
71
+ result = Hash.new
72
+ result[:key_blob] = buffer.read_string
73
+ result[:server_key] = Net::SSH::Buffer.new(result[:key_blob]).read_key
74
+ result[:server_ecdh_pubkey] = buffer.read_string
75
+
76
+ # compute shared secret from server's public key and client's private key
77
+ pk = OpenSSL::PKey::EC::Point.new(OpenSSL::PKey::EC.new(curve_name).group,
78
+ OpenSSL::BN.new(result[:server_ecdh_pubkey], 2))
79
+ result[:shared_secret] = OpenSSL::BN.new(ecdh.dh_compute_key(pk), 2)
80
+
81
+ sig_buffer = Net::SSH::Buffer.new(buffer.read_string)
82
+ sig_type = sig_buffer.read_string
83
+ if sig_type != algorithms.host_key
84
+ raise Net::SSH::Exception,
85
+ "host key algorithm mismatch for signature " +
86
+ "'#{sig_type}' != '#{algorithms.host_key}'"
87
+ end
88
+ result[:server_sig] = sig_buffer.read_string
89
+
90
+ return result
91
+ end
92
+ end
93
+ end; end; end; end
@@ -0,0 +1,13 @@
1
+ module Net; module SSH; module Transport; module Kex
2
+
3
+ # A key-exchange service implementing the "ecdh-sha2-nistp256"
4
+ # key-exchange algorithm. (defined in RFC 5656)
5
+ class EcdhSHA2NistP384 < EcdhSHA2NistP256
6
+ def digester
7
+ OpenSSL::Digest::SHA384
8
+ end
9
+ def curve_name
10
+ OpenSSL::PKey::EC::CurveNameAlias['nistp384']
11
+ end
12
+ end
13
+ end; end; end; end
@@ -0,0 +1,13 @@
1
+ module Net; module SSH; module Transport; module Kex
2
+
3
+ # A key-exchange service implementing the "ecdh-sha2-nistp521"
4
+ # key-exchange algorithm. (defined in RFC 5656)
5
+ class EcdhSHA2NistP521 < EcdhSHA2NistP256
6
+ def digester
7
+ OpenSSL::Digest::SHA512
8
+ end
9
+ def curve_name
10
+ OpenSSL::PKey::EC::CurveNameAlias['nistp521']
11
+ end
12
+ end
13
+ end; end; end; end
@@ -0,0 +1,26 @@
1
+ module Net; module SSH; module Transport
2
+ module KeyExpander
3
+
4
+ # Generate a key value in accordance with the SSH2 specification.
5
+ # (RFC4253 7.2. "Output from Key Exchange")
6
+ def self.expand_key(bytes, start, options={})
7
+ if bytes == 0
8
+ return ""
9
+ end
10
+
11
+ k = start[0, bytes]
12
+
13
+ digester = options[:digester] or raise 'No digester supplied'
14
+ shared = options[:shared] or raise 'No shared secret supplied'
15
+ hash = options[:hash] or raise 'No hash supplied'
16
+
17
+ while k.length < bytes
18
+ step = digester.digest(shared + hash + k)
19
+ bytes_needed = bytes - k.length
20
+ k << step[0, bytes_needed]
21
+ end
22
+
23
+ return k
24
+ end
25
+ end
26
+ end; end; end
@@ -0,0 +1,237 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'openssl'
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
+ if defined?(OpenSSL::PKey::EC)
127
+ # This class is originally defined in the OpenSSL module. As needed, methods
128
+ # have been added to it by the Net::SSH module for convenience in dealing
129
+ # with SSH functionality.
130
+ class EC
131
+ CurveNameAlias = {
132
+ "nistp256" => "prime256v1",
133
+ "nistp384" => "secp384r1",
134
+ "nistp521" => "secp521r1",
135
+ }
136
+
137
+ CurveNameAliasInv = {
138
+ "prime256v1" => "nistp256",
139
+ "secp384r1" => "nistp384",
140
+ "secp521r1" => "nistp521",
141
+ }
142
+
143
+ def self.read_keyblob(curve_name_in_type, buffer)
144
+ curve_name_in_key = buffer.read_string
145
+ unless curve_name_in_type == curve_name_in_key
146
+ raise Net::SSH::Exception, "curve name mismatched (`#{curve_name_in_key}' with `#{curve_name_in_type}')"
147
+ end
148
+ public_key_oct = buffer.read_string
149
+ begin
150
+ key = OpenSSL::PKey::EC.new(OpenSSL::PKey::EC::CurveNameAlias[curve_name_in_key])
151
+ group = key.group
152
+ point = OpenSSL::PKey::EC::Point.new(group, OpenSSL::BN.new(public_key_oct, 2))
153
+ key.public_key = point
154
+
155
+ return key
156
+ rescue OpenSSL::PKey::ECError => e
157
+ raise NotImplementedError, "unsupported key type `#{type}'"
158
+ end
159
+
160
+ end
161
+
162
+ # Returns the description of this key type used by the
163
+ # SSH2 protocol, like "ecdsa-sha2-nistp256"
164
+ def ssh_type
165
+ "ecdsa-sha2-#{CurveNameAliasInv[self.group.curve_name]}"
166
+ end
167
+
168
+ def digester
169
+ if self.group.curve_name =~ /^[a-z]+(\d+)\w*\z/
170
+ curve_size = $1.to_i
171
+ if curve_size <= 256
172
+ OpenSSL::Digest::SHA256.new
173
+ elsif curve_size <= 384
174
+ OpenSSL::Digest::SHA384.new
175
+ else
176
+ OpenSSL::Digest::SHA512.new
177
+ end
178
+ else
179
+ OpenSSL::Digest::SHA256.new
180
+ end
181
+ end
182
+ private :digester
183
+
184
+ # Converts the key to a blob, according to the SSH2 protocol.
185
+ def to_blob
186
+ @blob ||= Net::SSH::Buffer.from(:string, ssh_type,
187
+ :string, CurveNameAliasInv[self.group.curve_name],
188
+ :string, self.public_key.to_bn.to_s(2)).to_s
189
+ @blob
190
+ end
191
+
192
+ # Verifies the given signature matches the given data.
193
+ def ssh_do_verify(sig, data)
194
+ digest = digester.digest(data)
195
+ a1sig = nil
196
+
197
+ begin
198
+ sig_r_len = sig[0,4].unpack("H*")[0].to_i(16)
199
+ sig_l_len = sig[4+sig_r_len,4].unpack("H*")[0].to_i(16)
200
+
201
+ sig_r = sig[4,sig_r_len].unpack("H*")[0]
202
+ sig_s = sig[4+sig_r_len+4,sig_l_len].unpack("H*")[0]
203
+
204
+ a1sig = OpenSSL::ASN1::Sequence([
205
+ OpenSSL::ASN1::Integer(sig_r.to_i(16)),
206
+ OpenSSL::ASN1::Integer(sig_s.to_i(16)),
207
+ ])
208
+ rescue
209
+ end
210
+
211
+ if a1sig == nil
212
+ return false
213
+ else
214
+ dsa_verify_asn1(digest, a1sig.to_der)
215
+ end
216
+ end
217
+
218
+ # Returns the signature for the given data.
219
+ def ssh_do_sign(data)
220
+ digest = digester.digest(data)
221
+ sig = dsa_sign_asn1(digest)
222
+ a1sig = OpenSSL::ASN1.decode( sig )
223
+
224
+ sig_r = a1sig.value[0].value
225
+ sig_s = a1sig.value[1].value
226
+
227
+ return Net::SSH::Buffer.from(:bignum, sig_r, :bignum, sig_s).to_s
228
+ end
229
+ end
230
+ else
231
+ class OpenSSL::PKey::ECError < RuntimeError
232
+ # for compatibility with interpreters
233
+ # without EC support (i.e. JRuby)
234
+ end
235
+ end
236
+ end
237
+ end
@@ -0,0 +1,235 @@
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 ||=
63
+ if respond_to?(:getpeername)
64
+ addr = getpeername
65
+ Socket.getnameinfo(addr, Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV).first
66
+ else
67
+ "<no hostip for proxy command>"
68
+ end
69
+ end
70
+
71
+ # Returns true if the IO is available for reading, and false otherwise.
72
+ def available_for_read?
73
+ result = Net::SSH::Compat.io_select([self], nil, nil, 0)
74
+ result && result.first.any?
75
+ end
76
+
77
+ # Returns the next full packet. If the mode parameter is :nonblock (the
78
+ # default), then this will return immediately, whether a packet is
79
+ # available or not, and will return nil if there is no packet ready to be
80
+ # returned. If the mode parameter is :block, then this method will block
81
+ # until a packet is available.
82
+ def next_packet(mode=:nonblock)
83
+ case mode
84
+ when :nonblock then
85
+ fill if available_for_read?
86
+ poll_next_packet
87
+
88
+ when :block then
89
+ loop do
90
+ packet = poll_next_packet
91
+ return packet if packet
92
+
93
+ loop do
94
+ result = Net::SSH::Compat.io_select([self]) or next
95
+ break if result.first.any?
96
+ end
97
+
98
+ if fill <= 0
99
+ raise Net::SSH::Disconnect, "connection closed by remote host"
100
+ end
101
+ end
102
+
103
+ else
104
+ raise ArgumentError, "expected :block or :nonblock, got #{mode.inspect}"
105
+ end
106
+ end
107
+
108
+ # Enqueues a packet to be sent, and blocks until the entire packet is
109
+ # sent.
110
+ def send_packet(payload)
111
+ enqueue_packet(payload)
112
+ wait_for_pending_sends
113
+ end
114
+
115
+ # Enqueues a packet to be sent, but does not immediately send the packet.
116
+ # The given payload is pre-processed according to the algorithms specified
117
+ # in the client state (compression, cipher, and hmac).
118
+ def enqueue_packet(payload)
119
+ # try to compress the packet
120
+ payload = client.compress(payload)
121
+
122
+ # the length of the packet, minus the padding
123
+ actual_length = 4 + payload.length + 1
124
+
125
+ # compute the padding length
126
+ padding_length = client.block_size - (actual_length % client.block_size)
127
+ padding_length += client.block_size if padding_length < 4
128
+
129
+ # compute the packet length (sans the length field itself)
130
+ packet_length = payload.length + padding_length + 1
131
+
132
+ if packet_length < 16
133
+ padding_length += client.block_size
134
+ packet_length = payload.length + padding_length + 1
135
+ end
136
+
137
+ padding = Array.new(padding_length) { rand(256) }.pack("C*")
138
+
139
+ unencrypted_data = [packet_length, padding_length, payload, padding].pack("NCA*A*")
140
+ mac = client.hmac.digest([client.sequence_number, unencrypted_data].pack("NA*"))
141
+
142
+ encrypted_data = client.update_cipher(unencrypted_data) << client.final_cipher
143
+ message = encrypted_data + mac
144
+
145
+ debug { "queueing packet nr #{client.sequence_number} type #{payload.getbyte(0)} len #{packet_length}" }
146
+ enqueue(message)
147
+
148
+ client.increment(packet_length)
149
+
150
+ self
151
+ end
152
+
153
+ # Performs any pending cleanup necessary on the IO and its associated
154
+ # state objects. (See State#cleanup).
155
+ def cleanup
156
+ client.cleanup
157
+ server.cleanup
158
+ end
159
+
160
+ # If the IO object requires a rekey operation (as indicated by either its
161
+ # client or server state objects, see State#needs_rekey?), this will
162
+ # yield. Otherwise, this does nothing.
163
+ def if_needs_rekey?
164
+ if client.needs_rekey? || server.needs_rekey?
165
+ yield
166
+ client.reset! if client.needs_rekey?
167
+ server.reset! if server.needs_rekey?
168
+ end
169
+ end
170
+
171
+ protected
172
+
173
+ # Called when this module is used to extend an object. It initializes
174
+ # the states and generally prepares the object for use as a packet stream.
175
+ def initialize_ssh
176
+ @hints = {}
177
+ @server = State.new(self, :server)
178
+ @client = State.new(self, :client)
179
+ @packet = nil
180
+ initialize_buffered_io
181
+ end
182
+
183
+ # Tries to read the next packet. If there is insufficient data to read
184
+ # an entire packet, this returns immediately, otherwise the packet is
185
+ # read, post-processed according to the cipher, hmac, and compression
186
+ # algorithms specified in the server state object, and returned as a
187
+ # new Packet object.
188
+ def poll_next_packet
189
+ if @packet.nil?
190
+ minimum = server.block_size < 4 ? 4 : server.block_size
191
+ return nil if available < minimum
192
+ data = read_available(minimum)
193
+
194
+ # decipher it
195
+ @packet = Net::SSH::Buffer.new(server.update_cipher(data))
196
+ @packet_length = @packet.read_long
197
+ end
198
+
199
+ need = @packet_length + 4 - server.block_size
200
+ raise Net::SSH::Exception, "padding error, need #{need} block #{server.block_size}" if need % server.block_size != 0
201
+
202
+ return nil if available < need + server.hmac.mac_length
203
+
204
+ if need > 0
205
+ # read the remainder of the packet and decrypt it.
206
+ data = read_available(need)
207
+ @packet.append(server.update_cipher(data))
208
+ end
209
+
210
+ # get the hmac from the tail of the packet (if one exists), and
211
+ # then validate it.
212
+ real_hmac = read_available(server.hmac.mac_length) || ""
213
+
214
+ @packet.append(server.final_cipher)
215
+ padding_length = @packet.read_byte
216
+
217
+ payload = @packet.read(@packet_length - padding_length - 1)
218
+ padding = @packet.read(padding_length) if padding_length > 0
219
+
220
+ my_computed_hmac = server.hmac.digest([server.sequence_number, @packet.content].pack("NA*"))
221
+ raise Net::SSH::Exception, "corrupted mac detected" if real_hmac != my_computed_hmac
222
+
223
+ # try to decompress the payload, in case compression is active
224
+ payload = server.decompress(payload)
225
+
226
+ debug { "received packet nr #{server.sequence_number} type #{payload.getbyte(0)} len #{@packet_length}" }
227
+
228
+ server.increment(@packet_length)
229
+ @packet = nil
230
+
231
+ return Packet.new(payload)
232
+ end
233
+ end
234
+
235
+ end; end; end