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.IOException; |
29 import java.nio.*; |
29 import java.nio.ByteBuffer; |
30 import java.util.*; |
30 import java.util.LinkedList; |
31 |
|
32 import javax.net.ssl.SSLException; |
|
33 import javax.net.ssl.SSLHandshakeException; |
31 import javax.net.ssl.SSLHandshakeException; |
34 import sun.security.util.HexDumpEncoder; |
32 |
35 import static sun.security.ssl.Ciphertext.RecordType; |
33 import sun.security.ssl.SSLCipher.SSLWriteCipher; |
|
34 import sun.security.ssl.KeyUpdate.KeyUpdateMessage; |
|
35 import sun.security.ssl.KeyUpdate.KeyUpdateRequest; |
36 |
36 |
37 /** |
37 /** |
38 * {@code OutputRecord} implementation for {@code SSLEngine}. |
38 * {@code OutputRecord} implementation for {@code SSLEngine}. |
39 */ |
39 */ |
40 final class SSLEngineOutputRecord extends OutputRecord implements SSLRecord { |
40 final class SSLEngineOutputRecord extends OutputRecord implements SSLRecord { |
41 |
41 |
42 private HandshakeFragment fragmenter = null; |
42 private HandshakeFragment fragmenter = null; |
43 private LinkedList<RecordMemo> alertMemos = new LinkedList<>(); |
|
44 private boolean isTalkingToV2 = false; // SSLv2Hello |
43 private boolean isTalkingToV2 = false; // SSLv2Hello |
45 private ByteBuffer v2ClientHello = null; // SSLv2Hello |
44 private ByteBuffer v2ClientHello = null; // SSLv2Hello |
46 |
45 |
47 private boolean isCloseWaiting = false; |
46 private boolean isCloseWaiting = false; |
48 |
47 |
49 SSLEngineOutputRecord() { |
48 SSLEngineOutputRecord(HandshakeHash handshakeHash) { |
50 this.writeAuthenticator = MAC.TLS_NULL; |
49 super(handshakeHash, SSLWriteCipher.nullTlsWriteCipher()); |
51 |
50 |
52 this.packetSize = SSLRecord.maxRecordSize; |
51 this.packetSize = SSLRecord.maxRecordSize; |
53 this.protocolVersion = ProtocolVersion.DEFAULT_TLS; |
52 this.protocolVersion = ProtocolVersion.NONE; |
54 } |
53 } |
55 |
54 |
56 @Override |
55 @Override |
57 public synchronized void close() throws IOException { |
56 public synchronized void close() throws IOException { |
58 if (!isClosed) { |
57 if (!isClosed) { |
59 if (alertMemos != null && !alertMemos.isEmpty()) { |
58 if (fragmenter != null && fragmenter.hasAlert()) { |
60 isCloseWaiting = true; |
59 isCloseWaiting = true; |
61 } else { |
60 } else { |
62 super.close(); |
61 super.close(); |
63 } |
62 } |
64 } |
63 } |
65 } |
64 } |
66 |
65 |
67 @Override |
66 @Override |
68 void encodeAlert(byte level, byte description) throws IOException { |
67 void encodeAlert(byte level, byte description) throws IOException { |
69 RecordMemo memo = new RecordMemo(); |
68 if (fragmenter == null) { |
70 |
69 fragmenter = new HandshakeFragment(); |
71 memo.contentType = Record.ct_alert; |
70 } |
72 memo.majorVersion = protocolVersion.major; |
71 |
73 memo.minorVersion = protocolVersion.minor; |
72 fragmenter.queueUpAlert(level, description); |
74 memo.encodeCipher = writeCipher; |
|
75 memo.encodeAuthenticator = writeAuthenticator; |
|
76 |
|
77 memo.fragment = new byte[2]; |
|
78 memo.fragment[0] = level; |
|
79 memo.fragment[1] = description; |
|
80 |
|
81 alertMemos.add(memo); |
|
82 } |
73 } |
83 |
74 |
84 @Override |
75 @Override |
85 void encodeHandshake(byte[] source, |
76 void encodeHandshake(byte[] source, |
86 int offset, int length) throws IOException { |
77 int offset, int length) throws IOException { |
133 void encodeV2NoCipher() throws IOException { |
124 void encodeV2NoCipher() throws IOException { |
134 isTalkingToV2 = true; |
125 isTalkingToV2 = true; |
135 } |
126 } |
136 |
127 |
137 @Override |
128 @Override |
138 Ciphertext encode(ByteBuffer[] sources, int offset, int length, |
129 Ciphertext encode( |
|
130 ByteBuffer[] srcs, int srcsOffset, int srcsLength, |
|
131 ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException { |
|
132 return encode(srcs, srcsOffset, srcsLength, dsts[0]); |
|
133 } |
|
134 |
|
135 private Ciphertext encode(ByteBuffer[] sources, int offset, int length, |
139 ByteBuffer destination) throws IOException { |
136 ByteBuffer destination) throws IOException { |
140 |
137 |
141 if (writeAuthenticator.seqNumOverflow()) { |
138 if (writeCipher.authenticator.seqNumOverflow()) { |
142 if (debug != null && Debug.isOn("ssl")) { |
139 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { |
143 System.out.println(Thread.currentThread().getName() + |
140 SSLLogger.fine( |
144 ", sequence number extremely close to overflow " + |
141 "sequence number extremely close to overflow " + |
145 "(2^64-1 packets). Closing connection."); |
142 "(2^64-1 packets). Closing connection."); |
146 } |
143 } |
147 |
144 |
148 throw new SSLHandshakeException("sequence number overflow"); |
145 throw new SSLHandshakeException("sequence number overflow"); |
149 } |
146 } |
150 |
147 |
151 int macLen = 0; |
148 // Don't process the incoming record until all of the |
152 if (writeAuthenticator instanceof MAC) { |
149 // buffered records get handled. |
153 macLen = ((MAC)writeAuthenticator).MAClen(); |
150 Ciphertext ct = acquireCiphertext(destination); |
|
151 if (ct != null) { |
|
152 return ct; |
|
153 } |
|
154 |
|
155 if (sources == null || sources.length == 0) { |
|
156 return null; |
|
157 } |
|
158 |
|
159 int srcsRemains = 0; |
|
160 for (int i = offset; i < offset + length; i++) { |
|
161 srcsRemains += sources[i].remaining(); |
|
162 } |
|
163 |
|
164 if (srcsRemains == 0) { |
|
165 return null; |
154 } |
166 } |
155 |
167 |
156 int dstLim = destination.limit(); |
168 int dstLim = destination.limit(); |
157 boolean isFirstRecordOfThePayload = true; |
169 boolean isFirstRecordOfThePayload = true; |
158 int packetLeftSize = Math.min(maxRecordSize, packetSize); |
170 int packetLeftSize = Math.min(maxRecordSize, packetSize); |
206 } |
218 } |
207 |
219 |
208 destination.limit(destination.position()); |
220 destination.limit(destination.position()); |
209 destination.position(dstContent); |
221 destination.position(dstContent); |
210 |
222 |
211 if ((debug != null) && Debug.isOn("record")) { |
223 if (SSLLogger.isOn && SSLLogger.isOn("record")) { |
212 System.out.println(Thread.currentThread().getName() + |
224 SSLLogger.fine( |
213 ", WRITE: " + protocolVersion + " " + |
225 "WRITE: " + protocolVersion + " " + |
214 Record.contentName(Record.ct_application_data) + |
226 ContentType.APPLICATION_DATA.name + |
215 ", length = " + destination.remaining()); |
227 ", length = " + destination.remaining()); |
216 } |
228 } |
217 |
229 |
218 // Encrypt the fragment and wrap up a record. |
230 // Encrypt the fragment and wrap up a record. |
219 recordSN = encrypt(writeAuthenticator, writeCipher, |
231 recordSN = encrypt(writeCipher, |
220 Record.ct_application_data, destination, |
232 ContentType.APPLICATION_DATA.id, destination, |
221 dstPos, dstLim, headerSize, |
233 dstPos, dstLim, headerSize, |
222 protocolVersion, false); |
234 protocolVersion); |
223 |
235 |
224 if ((debug != null) && Debug.isOn("packet")) { |
236 if (SSLLogger.isOn && SSLLogger.isOn("packet")) { |
225 ByteBuffer temporary = destination.duplicate(); |
237 ByteBuffer temporary = destination.duplicate(); |
226 temporary.limit(temporary.position()); |
238 temporary.limit(temporary.position()); |
227 temporary.position(dstPos); |
239 temporary.position(dstPos); |
228 Debug.printHex( |
240 SSLLogger.fine("Raw write", temporary); |
229 "[Raw write]: length = " + temporary.remaining(), |
|
230 temporary); |
|
231 } |
241 } |
232 |
242 |
233 packetLeftSize -= destination.position() - dstPos; |
243 packetLeftSize -= destination.position() - dstPos; |
234 |
244 |
235 // remain the limit unchanged |
245 // remain the limit unchanged |
236 destination.limit(dstLim); |
246 destination.limit(dstLim); |
237 |
247 |
238 if (isFirstAppOutputRecord) { |
248 if (isFirstAppOutputRecord) { |
239 isFirstAppOutputRecord = false; |
249 isFirstAppOutputRecord = false; |
240 } |
250 } |
241 } |
251 |
242 |
252 // atKeyLimit() inactive when limits not checked, tc set when limits |
243 return new Ciphertext(RecordType.RECORD_APPLICATION_DATA, recordSN); |
253 // are active. |
244 } |
254 if (writeCipher.atKeyLimit()) { |
245 |
255 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { |
246 @Override |
256 SSLLogger.fine("KeyUpdate: triggered, write side."); |
247 Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException { |
257 } |
|
258 |
|
259 PostHandshakeContext p = new PostHandshakeContext(tc); |
|
260 KeyUpdate.handshakeProducer.produce(p, |
|
261 new KeyUpdateMessage(p, KeyUpdateRequest.REQUESTED)); |
|
262 } |
|
263 } |
|
264 |
|
265 return new Ciphertext(ContentType.APPLICATION_DATA.id, |
|
266 SSLHandshake.NOT_APPLICABLE.id, recordSN); |
|
267 } |
|
268 |
|
269 private Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException { |
248 if (isTalkingToV2) { // SSLv2Hello |
270 if (isTalkingToV2) { // SSLv2Hello |
249 // We don't support SSLv2. Send an SSLv2 error message |
271 // We don't support SSLv2. Send an SSLv2 error message |
250 // so that the connection can be closed gracefully. |
272 // so that the connection can be closed gracefully. |
251 // |
273 // |
252 // Please don't change the limit of the destination buffer. |
274 // Please don't change the limit of the destination buffer. |
253 destination.put(SSLRecord.v2NoCipher); |
275 destination.put(SSLRecord.v2NoCipher); |
254 if (debug != null && Debug.isOn("packet")) { |
276 if (SSLLogger.isOn && SSLLogger.isOn("packet")) { |
255 Debug.printHex( |
277 SSLLogger.fine("Raw write", SSLRecord.v2NoCipher); |
256 "[Raw write]: length = " + SSLRecord.v2NoCipher.length, |
|
257 SSLRecord.v2NoCipher); |
|
258 } |
278 } |
259 |
279 |
260 isTalkingToV2 = false; |
280 isTalkingToV2 = false; |
261 |
281 |
262 return new Ciphertext(RecordType.RECORD_ALERT, -1L); |
282 return new Ciphertext(ContentType.ALERT.id, |
|
283 SSLHandshake.NOT_APPLICABLE.id, -1L); |
263 } |
284 } |
264 |
285 |
265 if (v2ClientHello != null) { |
286 if (v2ClientHello != null) { |
266 // deliver the SSLv2 format ClientHello message |
287 // deliver the SSLv2 format ClientHello message |
267 // |
288 // |
268 // Please don't change the limit of the destination buffer. |
289 // Please don't change the limit of the destination buffer. |
269 if (debug != null) { |
290 if (SSLLogger.isOn) { |
270 if (Debug.isOn("record")) { |
291 if (SSLLogger.isOn("record")) { |
271 System.out.println(Thread.currentThread().getName() + |
292 SSLLogger.fine(Thread.currentThread().getName() + |
272 ", WRITE: SSLv2 ClientHello message" + |
293 ", WRITE: SSLv2 ClientHello message" + |
273 ", length = " + v2ClientHello.remaining()); |
294 ", length = " + v2ClientHello.remaining()); |
274 } |
295 } |
275 |
296 |
276 if (Debug.isOn("packet")) { |
297 if (SSLLogger.isOn("packet")) { |
277 Debug.printHex( |
298 SSLLogger.fine("Raw write", v2ClientHello); |
278 "[Raw write]: length = " + v2ClientHello.remaining(), |
|
279 v2ClientHello); |
|
280 } |
299 } |
281 } |
300 } |
282 |
301 |
283 destination.put(v2ClientHello); |
302 destination.put(v2ClientHello); |
284 v2ClientHello = null; |
303 v2ClientHello = null; |
285 |
304 |
286 return new Ciphertext(RecordType.RECORD_CLIENT_HELLO, -1L); |
305 return new Ciphertext(ContentType.HANDSHAKE.id, |
287 } |
306 SSLHandshake.CLIENT_HELLO.id, -1L); |
288 |
|
289 if (alertMemos != null && !alertMemos.isEmpty()) { |
|
290 RecordMemo memo = alertMemos.pop(); |
|
291 |
|
292 int macLen = 0; |
|
293 if (memo.encodeAuthenticator instanceof MAC) { |
|
294 macLen = ((MAC)memo.encodeAuthenticator).MAClen(); |
|
295 } |
|
296 |
|
297 int dstPos = destination.position(); |
|
298 int dstLim = destination.limit(); |
|
299 int dstContent = dstPos + headerSize + |
|
300 writeCipher.getExplicitNonceSize(); |
|
301 destination.position(dstContent); |
|
302 |
|
303 destination.put(memo.fragment); |
|
304 |
|
305 destination.limit(destination.position()); |
|
306 destination.position(dstContent); |
|
307 |
|
308 if ((debug != null) && Debug.isOn("record")) { |
|
309 System.out.println(Thread.currentThread().getName() + |
|
310 ", WRITE: " + protocolVersion + " " + |
|
311 Record.contentName(Record.ct_alert) + |
|
312 ", length = " + destination.remaining()); |
|
313 } |
|
314 |
|
315 // Encrypt the fragment and wrap up a record. |
|
316 long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher, |
|
317 Record.ct_alert, destination, dstPos, dstLim, headerSize, |
|
318 ProtocolVersion.valueOf(memo.majorVersion, |
|
319 memo.minorVersion), false); |
|
320 |
|
321 if ((debug != null) && Debug.isOn("packet")) { |
|
322 ByteBuffer temporary = destination.duplicate(); |
|
323 temporary.limit(temporary.position()); |
|
324 temporary.position(dstPos); |
|
325 Debug.printHex( |
|
326 "[Raw write]: length = " + temporary.remaining(), |
|
327 temporary); |
|
328 } |
|
329 |
|
330 // remain the limit unchanged |
|
331 destination.limit(dstLim); |
|
332 |
|
333 if (isCloseWaiting && (memo.contentType == Record.ct_alert)) { |
|
334 isCloseWaiting = true; |
|
335 close(); |
|
336 } |
|
337 return new Ciphertext(RecordType.RECORD_ALERT, recordSN); |
|
338 } |
307 } |
339 |
308 |
340 if (fragmenter != null) { |
309 if (fragmenter != null) { |
341 return fragmenter.acquireCiphertext(destination); |
310 return fragmenter.acquireCiphertext(destination); |
342 } |
311 } |
472 // still have space for more records? |
446 // still have space for more records? |
473 if ((remainingFragLen > chipLen) && |
447 if ((remainingFragLen > chipLen) && |
474 !handshakeMemos.isEmpty()) { |
448 !handshakeMemos.isEmpty()) { |
475 |
449 |
476 // look for the next buffered record fragment |
450 // look for the next buffered record fragment |
477 RecordMemo reMemo = handshakeMemos.getFirst(); |
451 RecordMemo rm = handshakeMemos.getFirst(); |
478 if (reMemo.contentType == Record.ct_handshake) { |
452 if (rm.contentType == ContentType.HANDSHAKE.id && |
479 hsMemo = (HandshakeMemo)reMemo; |
453 rm.encodeCipher == hsMemo.encodeCipher) { |
|
454 hsMemo = (HandshakeMemo)rm; |
480 } else { |
455 } else { |
481 // not handshake message, break the loop |
456 // not of the flight, break the loop |
482 break; |
457 break; |
483 } |
458 } |
484 } |
459 } |
485 } |
460 } |
486 |
461 |
487 remainingFragLen -= chipLen; |
462 remainingFragLen -= chipLen; |
488 } |
463 } |
489 |
|
490 fragLen -= remainingFragLen; |
|
491 } else { |
464 } else { |
492 fragLen = Math.min(fragLen, memo.fragment.length); |
465 fragLen = Math.min(fragLen, memo.fragment.length); |
493 dstBuf.put(memo.fragment, 0, fragLen); |
466 dstBuf.put(memo.fragment, 0, fragLen); |
494 |
467 |
495 handshakeMemos.removeFirst(); |
468 handshakeMemos.removeFirst(); |
496 } |
469 } |
497 |
470 |
498 dstBuf.limit(dstBuf.position()); |
471 dstBuf.limit(dstBuf.position()); |
499 dstBuf.position(dstContent); |
472 dstBuf.position(dstContent); |
500 |
473 |
501 if ((debug != null) && Debug.isOn("record")) { |
474 if (SSLLogger.isOn && SSLLogger.isOn("record")) { |
502 System.out.println(Thread.currentThread().getName() + |
475 SSLLogger.fine( |
503 ", WRITE: " + protocolVersion + " " + |
476 "WRITE: " + protocolVersion + " " + |
504 Record.contentName(memo.contentType) + |
477 ContentType.nameOf(memo.contentType) + |
505 ", length = " + dstBuf.remaining()); |
478 ", length = " + dstBuf.remaining()); |
506 } |
479 } |
507 |
480 |
508 // Encrypt the fragment and wrap up a record. |
481 // Encrypt the fragment and wrap up a record. |
509 long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher, |
482 long recordSN = encrypt( |
|
483 memo.encodeCipher, |
510 memo.contentType, dstBuf, |
484 memo.contentType, dstBuf, |
511 dstPos, dstLim, headerSize, |
485 dstPos, dstLim, headerSize, |
512 ProtocolVersion.valueOf(memo.majorVersion, |
486 ProtocolVersion.valueOf(memo.majorVersion, |
513 memo.minorVersion), false); |
487 memo.minorVersion)); |
514 |
488 |
515 if ((debug != null) && Debug.isOn("packet")) { |
489 if (SSLLogger.isOn && SSLLogger.isOn("packet")) { |
516 ByteBuffer temporary = dstBuf.duplicate(); |
490 ByteBuffer temporary = dstBuf.duplicate(); |
517 temporary.limit(temporary.position()); |
491 temporary.limit(temporary.position()); |
518 temporary.position(dstPos); |
492 temporary.position(dstPos); |
519 Debug.printHex( |
493 SSLLogger.fine("Raw write", temporary); |
520 "[Raw write]: length = " + temporary.remaining(), |
|
521 temporary); |
|
522 } |
494 } |
523 |
495 |
524 // remain the limit unchanged |
496 // remain the limit unchanged |
525 dstBuf.limit(dstLim); |
497 dstBuf.limit(dstLim); |
526 |
498 |
527 // Reset the fragmentation offset. |
499 // Reset the fragmentation offset. |
528 if (hsMemo != null) { |
500 if (hsMemo != null) { |
529 return new Ciphertext(RecordType.valueOf( |
501 return new Ciphertext(hsMemo.contentType, |
530 hsMemo.contentType, hsMemo.handshakeType), recordSN); |
502 hsMemo.handshakeType, recordSN); |
531 } else { |
503 } else { |
532 return new Ciphertext( |
504 if (isCloseWaiting && |
533 RecordType.RECORD_CHANGE_CIPHER_SPEC, recordSN); |
505 memo.contentType == ContentType.ALERT.id) { |
|
506 close(); |
|
507 } |
|
508 |
|
509 return new Ciphertext(memo.contentType, |
|
510 SSLHandshake.NOT_APPLICABLE.id, recordSN); |
534 } |
511 } |
535 } |
512 } |
536 |
513 |
537 boolean isEmpty() { |
514 boolean isEmpty() { |
538 return handshakeMemos.isEmpty(); |
515 return handshakeMemos.isEmpty(); |
|
516 } |
|
517 |
|
518 boolean hasAlert() { |
|
519 for (RecordMemo memo : handshakeMemos) { |
|
520 if (memo.contentType == ContentType.ALERT.id) { |
|
521 return true; |
|
522 } |
|
523 } |
|
524 |
|
525 return false; |
539 } |
526 } |
540 } |
527 } |
541 |
528 |
542 /* |
529 /* |
543 * Need to split the payload except the following cases: |
530 * Need to split the payload except the following cases: |