25 |
25 |
26 package sun.security.ssl; |
26 package sun.security.ssl; |
27 |
27 |
28 import java.io.*; |
28 import java.io.*; |
29 import java.nio.*; |
29 import java.nio.*; |
30 |
30 import java.security.GeneralSecurityException; |
|
31 import java.util.ArrayList; |
31 import javax.crypto.BadPaddingException; |
32 import javax.crypto.BadPaddingException; |
32 |
|
33 import javax.net.ssl.*; |
33 import javax.net.ssl.*; |
34 |
34 import sun.security.ssl.SSLCipher.SSLReadCipher; |
35 import sun.security.util.HexDumpEncoder; |
|
36 |
|
37 |
35 |
38 /** |
36 /** |
39 * {@code InputRecord} implementation for {@code SSLSocket}. |
37 * {@code InputRecord} implementation for {@code SSLSocket}. |
40 * |
38 * |
41 * @author David Brownell |
39 * @author David Brownell |
42 */ |
40 */ |
43 final class SSLSocketInputRecord extends InputRecord implements SSLRecord { |
41 final class SSLSocketInputRecord extends InputRecord implements SSLRecord { |
44 private OutputStream deliverStream = null; |
42 private InputStream is = null; |
45 private byte[] temporary = new byte[1024]; |
43 private OutputStream os = null; |
|
44 private final byte[] temporary = new byte[1024]; |
46 |
45 |
47 // used by handshake hash computation for handshake fragment |
46 // used by handshake hash computation for handshake fragment |
48 private byte prevType = -1; |
47 private byte prevType = -1; |
49 private int hsMsgOff = 0; |
48 private int hsMsgOff = 0; |
50 private int hsMsgLen = 0; |
49 private int hsMsgLen = 0; |
51 |
50 |
52 private boolean formatVerified = false; // SSLv2 ruled out? |
51 private boolean formatVerified = false; // SSLv2 ruled out? |
53 |
52 |
|
53 // Cache for incomplete handshake messages. |
|
54 private ByteBuffer handshakeBuffer = null; |
|
55 |
54 private boolean hasHeader = false; // Had read the record header |
56 private boolean hasHeader = false; // Had read the record header |
55 |
57 |
56 SSLSocketInputRecord() { |
58 SSLSocketInputRecord(HandshakeHash handshakeHash) { |
57 this.readAuthenticator = MAC.TLS_NULL; |
59 super(handshakeHash, SSLReadCipher.nullTlsReadCipher()); |
58 } |
60 } |
59 |
61 |
60 @Override |
62 @Override |
61 int bytesInCompletePacket(InputStream is) throws IOException { |
63 int bytesInCompletePacket() throws IOException { |
62 |
64 |
63 if (!hasHeader) { |
65 if (!hasHeader) { |
64 // read exactly one record |
66 // read exactly one record |
65 int really = read(is, temporary, 0, headerSize); |
67 int really = read(is, temporary, 0, headerSize); |
66 if (really < 0) { |
68 if (really < 0) { |
67 throw new EOFException("SSL peer shut down incorrectly"); |
69 // EOF: peer shut down incorrectly |
|
70 return -1; |
68 } |
71 } |
69 hasHeader = true; |
72 hasHeader = true; |
70 } |
73 } |
71 |
74 |
72 byte byteZero = temporary[0]; |
75 byte byteZero = temporary[0]; |
77 * ignore the verifications steps, and jump right to the |
80 * ignore the verifications steps, and jump right to the |
78 * determination. Otherwise, try one last hueristic to |
81 * determination. Otherwise, try one last hueristic to |
79 * see if it's SSL/TLS. |
82 * see if it's SSL/TLS. |
80 */ |
83 */ |
81 if (formatVerified || |
84 if (formatVerified || |
82 (byteZero == ct_handshake) || (byteZero == ct_alert)) { |
85 (byteZero == ContentType.HANDSHAKE.id) || |
|
86 (byteZero == ContentType.ALERT.id)) { |
83 /* |
87 /* |
84 * Last sanity check that it's not a wild record |
88 * Last sanity check that it's not a wild record |
85 */ |
89 */ |
86 ProtocolVersion recordVersion = |
90 if (!ProtocolVersion.isNegotiable( |
87 ProtocolVersion.valueOf(temporary[1], temporary[2]); |
91 temporary[1], temporary[2], false, false)) { |
88 |
92 throw new SSLException("Unrecognized record version " + |
89 // check the record version |
93 ProtocolVersion.nameOf(temporary[1], temporary[2]) + |
90 checkRecordVersion(recordVersion, false); |
94 " , plaintext connection?"); |
|
95 } |
91 |
96 |
92 /* |
97 /* |
93 * Reasonably sure this is a V3, disable further checks. |
98 * Reasonably sure this is a V3, disable further checks. |
94 * We can't do the same in the v2 check below, because |
99 * We can't do the same in the v2 check below, because |
95 * read still needs to parse/handle the v2 clientHello. |
100 * read still needs to parse/handle the v2 clientHello. |
165 /* |
171 /* |
166 * The first record must either be a handshake record or an |
172 * The first record must either be a handshake record or an |
167 * alert message. If it's not, it is either invalid or an |
173 * alert message. If it's not, it is either invalid or an |
168 * SSLv2 message. |
174 * SSLv2 message. |
169 */ |
175 */ |
170 if ((temporary[0] != ct_handshake) && |
176 if ((temporary[0] != ContentType.HANDSHAKE.id) && |
171 (temporary[0] != ct_alert)) { |
177 (temporary[0] != ContentType.ALERT.id)) { |
172 |
178 hasHeader = false; |
173 plaintext = handleUnknownRecord(is, temporary, destination); |
179 return handleUnknownRecord(temporary); |
174 } |
180 } |
175 } |
|
176 |
|
177 if (plaintext == null) { |
|
178 plaintext = decodeInputRecord(is, temporary, destination); |
|
179 } |
181 } |
180 |
182 |
181 // The record header should has comsumed. |
183 // The record header should has comsumed. |
182 hasHeader = false; |
184 hasHeader = false; |
183 |
185 return decodeInputRecord(temporary); |
184 return plaintext; |
186 } |
|
187 |
|
188 @Override |
|
189 void setReceiverStream(InputStream inputStream) { |
|
190 this.is = inputStream; |
185 } |
191 } |
186 |
192 |
187 @Override |
193 @Override |
188 void setDeliverStream(OutputStream outputStream) { |
194 void setDeliverStream(OutputStream outputStream) { |
189 this.deliverStream = outputStream; |
195 this.os = outputStream; |
190 } |
196 } |
191 |
197 |
192 // Note that destination may be null |
198 // Note that destination may be null |
193 private Plaintext decodeInputRecord(InputStream is, byte[] header, |
199 private Plaintext[] decodeInputRecord( |
194 ByteBuffer destination) throws IOException, BadPaddingException { |
200 byte[] header) throws IOException, BadPaddingException { |
195 |
201 byte contentType = header[0]; // pos: 0 |
196 byte contentType = header[0]; |
202 byte majorVersion = header[1]; // pos: 1 |
197 byte majorVersion = header[1]; |
203 byte minorVersion = header[2]; // pos: 2 |
198 byte minorVersion = header[2]; |
204 int contentLen = ((header[3] & 0xFF) << 8) + |
199 int contentLen = ((header[3] & 0xFF) << 8) + (header[4] & 0xFF); |
205 (header[4] & 0xFF); // pos: 3, 4 |
|
206 |
|
207 if (SSLLogger.isOn && SSLLogger.isOn("record")) { |
|
208 SSLLogger.fine( |
|
209 "READ: " + |
|
210 ProtocolVersion.nameOf(majorVersion, minorVersion) + |
|
211 " " + ContentType.nameOf(contentType) + ", length = " + |
|
212 contentLen); |
|
213 } |
200 |
214 |
201 // |
215 // |
202 // Check for upper bound. |
216 // Check for upper bound. |
203 // |
217 // |
204 // Note: May check packetSize limit in the future. |
218 // Note: May check packetSize limit in the future. |
227 contentLen -= howmuch; |
238 contentLen -= howmuch; |
228 } |
239 } |
229 destination.flip(); |
240 destination.flip(); |
230 destination.position(dstPos + headerSize); |
241 destination.position(dstPos + headerSize); |
231 |
242 |
232 if (debug != null && Debug.isOn("record")) { |
243 if (SSLLogger.isOn && SSLLogger.isOn("record")) { |
233 System.out.println(Thread.currentThread().getName() + |
244 SSLLogger.fine( |
234 ", READ: " + |
245 "READ: " + |
235 ProtocolVersion.valueOf(majorVersion, minorVersion) + |
246 ProtocolVersion.nameOf(majorVersion, minorVersion) + |
236 " " + Record.contentName(contentType) + ", length = " + |
247 " " + ContentType.nameOf(contentType) + ", length = " + |
237 destination.remaining()); |
248 destination.remaining()); |
238 } |
249 } |
239 |
250 |
240 // |
251 // |
241 // Decrypt the fragment |
252 // Decrypt the fragment |
242 // |
253 // |
243 ByteBuffer plaintext = |
254 ByteBuffer fragment; |
244 decrypt(readAuthenticator, readCipher, contentType, destination); |
255 try { |
245 |
256 Plaintext plaintext = |
246 if ((contentType != ct_handshake) && (hsMsgOff != hsMsgLen)) { |
257 readCipher.decrypt(contentType, destination, null); |
|
258 fragment = plaintext.fragment; |
|
259 contentType = plaintext.contentType; |
|
260 } catch (BadPaddingException bpe) { |
|
261 throw bpe; |
|
262 } catch (GeneralSecurityException gse) { |
|
263 throw (SSLProtocolException)(new SSLProtocolException( |
|
264 "Unexpected exception")).initCause(gse); |
|
265 } |
|
266 if (contentType != ContentType.HANDSHAKE.id && hsMsgOff != hsMsgLen) { |
247 throw new SSLProtocolException( |
267 throw new SSLProtocolException( |
248 "Expected to get a handshake fragment"); |
268 "Expected to get a handshake fragment"); |
249 } |
269 } |
250 |
270 |
251 // |
271 // |
252 // handshake hashing |
272 // parse handshake messages |
253 // |
273 // |
254 if (contentType == ct_handshake) { |
274 if (contentType == ContentType.HANDSHAKE.id) { |
255 int pltPos = plaintext.position(); |
275 ByteBuffer handshakeFrag = fragment; |
256 int pltLim = plaintext.limit(); |
276 if ((handshakeBuffer != null) && |
257 int frgPos = pltPos; |
277 (handshakeBuffer.remaining() != 0)) { |
258 for (int remains = plaintext.remaining(); remains > 0;) { |
278 ByteBuffer bb = ByteBuffer.wrap(new byte[ |
259 int howmuch; |
279 handshakeBuffer.remaining() + fragment.remaining()]); |
260 byte handshakeType; |
280 bb.put(handshakeBuffer); |
261 if (hsMsgOff < hsMsgLen) { |
281 bb.put(fragment); |
262 // a fragment of the handshake message |
282 handshakeFrag = bb.rewind(); |
263 howmuch = Math.min((hsMsgLen - hsMsgOff), remains); |
283 handshakeBuffer = null; |
264 handshakeType = prevType; |
284 } |
265 |
285 |
266 hsMsgOff += howmuch; |
286 ArrayList<Plaintext> plaintexts = new ArrayList<>(5); |
267 if (hsMsgOff == hsMsgLen) { |
287 while (handshakeFrag.hasRemaining()) { |
268 // Now is a complete handshake message. |
288 int remaining = handshakeFrag.remaining(); |
269 hsMsgOff = 0; |
289 if (remaining < handshakeHeaderSize) { |
270 hsMsgLen = 0; |
290 handshakeBuffer = ByteBuffer.wrap(new byte[remaining]); |
|
291 handshakeBuffer.put(handshakeFrag); |
|
292 handshakeBuffer.rewind(); |
|
293 break; |
|
294 } |
|
295 |
|
296 handshakeFrag.mark(); |
|
297 // skip the first byte: handshake type |
|
298 byte handshakeType = handshakeFrag.get(); |
|
299 int handshakeBodyLen = Record.getInt24(handshakeFrag); |
|
300 handshakeFrag.reset(); |
|
301 int handshakeMessageLen = |
|
302 handshakeHeaderSize + handshakeBodyLen; |
|
303 if (remaining < handshakeMessageLen) { |
|
304 handshakeBuffer = ByteBuffer.wrap(new byte[remaining]); |
|
305 handshakeBuffer.put(handshakeFrag); |
|
306 handshakeBuffer.rewind(); |
|
307 break; |
|
308 } if (remaining == handshakeMessageLen) { |
|
309 if (handshakeHash.isHashable(handshakeType)) { |
|
310 handshakeHash.receive(handshakeFrag); |
271 } |
311 } |
272 } else { // hsMsgOff == hsMsgLen, a new handshake message |
312 |
273 handshakeType = plaintext.get(); |
313 plaintexts.add( |
274 int handshakeLen = ((plaintext.get() & 0xFF) << 16) | |
314 new Plaintext(contentType, |
275 ((plaintext.get() & 0xFF) << 8) | |
315 majorVersion, minorVersion, -1, -1L, handshakeFrag) |
276 (plaintext.get() & 0xFF); |
316 ); |
277 plaintext.position(frgPos); |
317 break; |
278 if (remains < (handshakeLen + 1)) { // 1: handshake type |
318 } else { |
279 // This handshake message is fragmented. |
319 int fragPos = handshakeFrag.position(); |
280 prevType = handshakeType; |
320 int fragLim = handshakeFrag.limit(); |
281 hsMsgOff = remains - 4; // 4: handshake header |
321 int nextPos = fragPos + handshakeMessageLen; |
282 hsMsgLen = handshakeLen; |
322 handshakeFrag.limit(nextPos); |
|
323 |
|
324 if (handshakeHash.isHashable(handshakeType)) { |
|
325 handshakeHash.receive(handshakeFrag); |
283 } |
326 } |
284 |
327 |
285 howmuch = Math.min(handshakeLen + 4, remains); |
328 plaintexts.add( |
286 } |
329 new Plaintext(contentType, majorVersion, minorVersion, |
287 |
330 -1, -1L, handshakeFrag.slice()) |
288 plaintext.limit(frgPos + howmuch); |
331 ); |
289 |
332 |
290 if (handshakeType == HandshakeMessage.ht_hello_request) { |
333 handshakeFrag.position(nextPos); |
291 // omitted from handshake hash computation |
334 handshakeFrag.limit(fragLim); |
292 } else if ((handshakeType != HandshakeMessage.ht_finished) && |
335 } |
293 (handshakeType != HandshakeMessage.ht_certificate_verify)) { |
336 } |
294 |
337 |
295 if (handshakeHash == null) { |
338 return plaintexts.toArray(new Plaintext[0]); |
296 // used for cache only |
339 } |
297 handshakeHash = new HandshakeHash(false); |
340 |
298 } |
341 return new Plaintext[] { |
299 handshakeHash.update(plaintext); |
342 new Plaintext(contentType, |
300 } else { |
343 majorVersion, minorVersion, -1, -1L, fragment) |
301 // Reserve until this handshake message has been processed. |
344 // recordEpoch, recordSeq, plaintext); |
302 if (handshakeHash == null) { |
345 }; |
303 // used for cache only |
346 } |
304 handshakeHash = new HandshakeHash(false); |
347 |
305 } |
348 private Plaintext[] handleUnknownRecord( |
306 handshakeHash.reserve(plaintext); |
349 byte[] header) throws IOException, BadPaddingException { |
307 } |
|
308 |
|
309 plaintext.position(frgPos + howmuch); |
|
310 plaintext.limit(pltLim); |
|
311 |
|
312 frgPos += howmuch; |
|
313 remains -= howmuch; |
|
314 } |
|
315 plaintext.position(pltPos); |
|
316 } |
|
317 |
|
318 return new Plaintext(contentType, |
|
319 majorVersion, minorVersion, -1, -1L, plaintext); |
|
320 // recordEpoch, recordSeq, plaintext); |
|
321 } |
|
322 |
|
323 private Plaintext handleUnknownRecord(InputStream is, byte[] header, |
|
324 ByteBuffer destination) throws IOException, BadPaddingException { |
|
325 |
|
326 byte firstByte = header[0]; |
350 byte firstByte = header[0]; |
327 byte thirdByte = header[2]; |
351 byte thirdByte = header[2]; |
328 |
352 |
329 // Does it look like a Version 2 client hello (V2ClientHello)? |
353 // Does it look like a Version 2 client hello (V2ClientHello)? |
330 if (((firstByte & 0x80) != 0) && (thirdByte == 1)) { |
354 if (((firstByte & 0x80) != 0) && (thirdByte == 1)) { |
345 * Looks like a V2 client hello, but not one saying |
369 * Looks like a V2 client hello, but not one saying |
346 * "let's talk SSLv3". So we need to send an SSLv2 |
370 * "let's talk SSLv3". So we need to send an SSLv2 |
347 * error message, one that's treated as fatal by |
371 * error message, one that's treated as fatal by |
348 * clients (Otherwise we'll hang.) |
372 * clients (Otherwise we'll hang.) |
349 */ |
373 */ |
350 deliverStream.write(SSLRecord.v2NoCipher); // SSLv2Hello |
374 os.write(SSLRecord.v2NoCipher); // SSLv2Hello |
351 |
375 |
352 if (debug != null) { |
376 if (SSLLogger.isOn) { |
353 if (Debug.isOn("record")) { |
377 if (SSLLogger.isOn("record")) { |
354 System.out.println(Thread.currentThread().getName() + |
378 SSLLogger.fine( |
355 "Requested to negotiate unsupported SSLv2!"); |
379 "Requested to negotiate unsupported SSLv2!"); |
356 } |
380 } |
357 |
381 |
358 if (Debug.isOn("packet")) { |
382 if (SSLLogger.isOn("packet")) { |
359 Debug.printHex( |
383 SSLLogger.fine("Raw write", SSLRecord.v2NoCipher); |
360 "[Raw write]: length = " + |
|
361 SSLRecord.v2NoCipher.length, |
|
362 SSLRecord.v2NoCipher); |
|
363 } |
384 } |
364 } |
385 } |
365 |
386 |
366 throw new SSLException("Unsupported SSL v2.0 ClientHello"); |
387 throw new SSLException("Unsupported SSL v2.0 ClientHello"); |
367 } |
388 } |
368 |
389 |
369 int msgLen = ((header[0] & 0x7F) << 8) | (header[1] & 0xFF); |
390 int msgLen = ((header[0] & 0x7F) << 8) | (header[1] & 0xFF); |
370 |
391 |
371 if (destination == null) { |
392 ByteBuffer destination = ByteBuffer.allocate(headerSize + msgLen); |
372 destination = ByteBuffer.allocate(headerSize + msgLen); |
|
373 } |
|
374 destination.put(temporary, 0, headerSize); |
393 destination.put(temporary, 0, headerSize); |
375 msgLen -= 3; // had read 3 bytes of content as header |
394 msgLen -= 3; // had read 3 bytes of content as header |
376 while (msgLen > 0) { |
395 while (msgLen > 0) { |
377 int howmuch = Math.min(temporary.length, msgLen); |
396 int howmuch = Math.min(temporary.length, msgLen); |
378 int really = read(is, temporary, 0, howmuch); |
397 int really = read(is, temporary, 0, howmuch); |
389 * If we can map this into a V3 ClientHello, read and |
408 * If we can map this into a V3 ClientHello, read and |
390 * hash the rest of the V2 handshake, turn it into a |
409 * hash the rest of the V2 handshake, turn it into a |
391 * V3 ClientHello message, and pass it up. |
410 * V3 ClientHello message, and pass it up. |
392 */ |
411 */ |
393 destination.position(2); // exclude the header |
412 destination.position(2); // exclude the header |
394 |
413 handshakeHash.receive(destination); |
395 if (handshakeHash == null) { |
|
396 // used for cache only |
|
397 handshakeHash = new HandshakeHash(false); |
|
398 } |
|
399 handshakeHash.update(destination); |
|
400 destination.position(0); |
414 destination.position(0); |
401 |
415 |
402 ByteBuffer converted = convertToClientHello(destination); |
416 ByteBuffer converted = convertToClientHello(destination); |
403 |
417 |
404 if (debug != null && Debug.isOn("packet")) { |
418 if (SSLLogger.isOn && SSLLogger.isOn("packet")) { |
405 Debug.printHex( |
419 SSLLogger.fine( |
406 "[Converted] ClientHello", converted); |
420 "[Converted] ClientHello", converted); |
407 } |
421 } |
408 |
422 |
409 return new Plaintext(ct_handshake, |
423 return new Plaintext[] { |
410 majorVersion, minorVersion, -1, -1L, converted); |
424 new Plaintext(ContentType.HANDSHAKE.id, |
|
425 majorVersion, minorVersion, -1, -1L, converted) |
|
426 }; |
411 } else { |
427 } else { |
412 if (((firstByte & 0x80) != 0) && (thirdByte == 4)) { |
428 if (((firstByte & 0x80) != 0) && (thirdByte == 4)) { |
413 throw new SSLException("SSL V2.0 servers are not supported."); |
429 throw new SSLException("SSL V2.0 servers are not supported."); |
414 } |
430 } |
415 |
431 |