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