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