|
1 /* |
|
2 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. Oracle designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Oracle in the LICENSE file that accompanied this code. |
|
10 * |
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 * version 2 for more details (a copy is included in the LICENSE file that |
|
15 * accompanied this code). |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License version |
|
18 * 2 along with this work; if not, write to the Free Software Foundation, |
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 * |
|
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
22 * or visit www.oracle.com if you need additional information or have any |
|
23 * questions. |
|
24 */ |
|
25 |
|
26 package sun.security.ssl; |
|
27 |
|
28 import java.io.Closeable; |
|
29 import java.io.IOException; |
|
30 import java.security.AccessControlContext; |
|
31 import java.security.AccessController; |
|
32 import java.security.PrivilegedAction; |
|
33 import java.util.HashMap; |
|
34 import java.util.HashSet; |
|
35 import java.util.List; |
|
36 import java.util.Map; |
|
37 import java.util.Set; |
|
38 import javax.crypto.SecretKey; |
|
39 import javax.net.ssl.HandshakeCompletedEvent; |
|
40 import javax.net.ssl.HandshakeCompletedListener; |
|
41 import javax.net.ssl.SSLEngineResult.HandshakeStatus; |
|
42 import javax.net.ssl.SSLException; |
|
43 import javax.net.ssl.SSLSocket; |
|
44 import sun.security.ssl.SupportedGroupsExtension.NamedGroup; |
|
45 |
|
46 /** |
|
47 * SSL/(D)TLS transportation context. |
|
48 */ |
|
49 class TransportContext implements ConnectionContext, Closeable { |
|
50 final SSLTransport transport; |
|
51 |
|
52 // registered plaintext consumers |
|
53 final Map<Byte, SSLConsumer> consumers; |
|
54 final AccessControlContext acc; |
|
55 |
|
56 final SSLContextImpl sslContext; |
|
57 final SSLConfiguration sslConfig; |
|
58 final InputRecord inputRecord; |
|
59 final OutputRecord outputRecord; |
|
60 |
|
61 // connection status |
|
62 boolean isUnsureMode; |
|
63 boolean isNegotiated; |
|
64 boolean isBroken; |
|
65 boolean isInputCloseNotified; |
|
66 boolean isOutputCloseNotified; |
|
67 Exception closeReason; //SSLException or RuntimeException |
|
68 |
|
69 // negotiated security parameters |
|
70 SSLSessionImpl conSession; |
|
71 ProtocolVersion protocolVersion; |
|
72 String applicationProtocol; |
|
73 |
|
74 // handshake context |
|
75 HandshakeContext handshakeContext; |
|
76 |
|
77 // connection reserved status for handshake. |
|
78 boolean secureRenegotiation; |
|
79 byte[] clientVerifyData; |
|
80 byte[] serverVerifyData; |
|
81 |
|
82 // connection sensitive configuration |
|
83 List<NamedGroup> serverRequestedNamedGroups; |
|
84 |
|
85 SecretKey baseWriteSecret, baseReadSecret; |
|
86 CipherSuite cipherSuite; |
|
87 |
|
88 // Please never use the transport parameter other than storing a |
|
89 // reference to this object. |
|
90 TransportContext(SSLContextImpl sslContext, SSLTransport transport, |
|
91 InputRecord inputRecord, OutputRecord outputRecord) { |
|
92 this.transport = transport; |
|
93 this.sslContext = sslContext; |
|
94 this.inputRecord = inputRecord; |
|
95 this.outputRecord = outputRecord; |
|
96 this.sslConfig = new SSLConfiguration(sslContext, true); |
|
97 this.sslConfig.maximumPacketSize = outputRecord.getMaxPacketSize(); |
|
98 this.isUnsureMode = true; |
|
99 |
|
100 initialize(); |
|
101 |
|
102 this.acc = AccessController.getContext(); |
|
103 this.consumers = new HashMap<>(); |
|
104 } |
|
105 |
|
106 // Please never use the transport parameter other than storing a |
|
107 // reference to this object. |
|
108 TransportContext(SSLContextImpl sslContext, SSLTransport transport, |
|
109 InputRecord inputRecord, OutputRecord outputRecord, |
|
110 boolean isClientMode) { |
|
111 this.transport = transport; |
|
112 this.sslContext = sslContext; |
|
113 this.inputRecord = inputRecord; |
|
114 this.outputRecord = outputRecord; |
|
115 this.sslConfig = new SSLConfiguration(sslContext, isClientMode); |
|
116 this.sslConfig.maximumPacketSize = outputRecord.getMaxPacketSize(); |
|
117 this.isUnsureMode = false; |
|
118 |
|
119 initialize(); |
|
120 |
|
121 this.acc = AccessController.getContext(); |
|
122 this.consumers = new HashMap<>(); |
|
123 } |
|
124 |
|
125 // Please never use the transport parameter other than storing a |
|
126 // reference to this object. |
|
127 TransportContext(SSLContextImpl sslContext, SSLTransport transport, |
|
128 SSLConfiguration sslConfig, |
|
129 InputRecord inputRecord, OutputRecord outputRecord) { |
|
130 this.transport = transport; |
|
131 this.sslContext = sslContext; |
|
132 this.inputRecord = inputRecord; |
|
133 this.outputRecord = outputRecord; |
|
134 this.sslConfig = (SSLConfiguration)sslConfig.clone(); |
|
135 if (this.sslConfig.maximumPacketSize == 0) { |
|
136 this.sslConfig.maximumPacketSize = outputRecord.getMaxPacketSize(); |
|
137 } |
|
138 this.isUnsureMode = false; |
|
139 |
|
140 initialize(); |
|
141 |
|
142 this.acc = AccessController.getContext(); |
|
143 this.consumers = new HashMap<>(); |
|
144 } |
|
145 |
|
146 // Initialize the non-final class variables. |
|
147 private void initialize() { |
|
148 // initial security parameters |
|
149 this.conSession = SSLSessionImpl.nullSession; |
|
150 this.protocolVersion = this.sslConfig.maximumProtocolVersion; |
|
151 this.applicationProtocol = null; |
|
152 |
|
153 // initial handshake context |
|
154 this.handshakeContext = null; |
|
155 |
|
156 // initial security parameters for secure renegotiation |
|
157 this.secureRenegotiation = false; |
|
158 this.clientVerifyData = new byte[0]; |
|
159 this.serverVerifyData = new byte[0]; |
|
160 |
|
161 this.isNegotiated = false; |
|
162 this.isBroken = false; |
|
163 this.isInputCloseNotified = false; |
|
164 this.isOutputCloseNotified = false; |
|
165 this.closeReason = null; |
|
166 } |
|
167 |
|
168 // Dispatch plaintext to a specific consumer. |
|
169 void dispatch(Plaintext plaintext) throws IOException { |
|
170 if (plaintext == null) { |
|
171 return; |
|
172 } |
|
173 |
|
174 ContentType ct = ContentType.valueOf(plaintext.contentType); |
|
175 if (ct == null) { |
|
176 fatal(Alert.UNEXPECTED_MESSAGE, |
|
177 "Unknown content type: " + plaintext.contentType); |
|
178 return; // make compiler happy |
|
179 } |
|
180 |
|
181 switch (ct) { |
|
182 case HANDSHAKE: |
|
183 if (handshakeContext == null) { |
|
184 handshakeContext = sslConfig.isClientMode ? |
|
185 new ClientHandshakeContext(sslContext, this) : |
|
186 new ServerHandshakeContext(sslContext, this); |
|
187 outputRecord.initHandshaker(); |
|
188 } |
|
189 handshakeContext.dispatch(plaintext); |
|
190 break; |
|
191 case ALERT: |
|
192 Alert.alertConsumer.consume(this, plaintext.fragment); |
|
193 break; |
|
194 default: |
|
195 SSLConsumer consumer = consumers.get(plaintext.contentType); |
|
196 if (consumer != null) { |
|
197 consumer.consume(this, plaintext.fragment); |
|
198 } else { |
|
199 fatal(Alert.UNEXPECTED_MESSAGE, |
|
200 "Unexpected content: " + plaintext.contentType); |
|
201 } |
|
202 } |
|
203 } |
|
204 |
|
205 void kickstart() throws IOException { |
|
206 if (isUnsureMode) { |
|
207 throw new IllegalStateException("Client/Server mode not yet set."); |
|
208 } |
|
209 |
|
210 // initialize the handshaker if necessary |
|
211 if (handshakeContext == null) { |
|
212 handshakeContext = sslConfig.isClientMode ? |
|
213 new ClientHandshakeContext(sslContext, this) : |
|
214 new ServerHandshakeContext(sslContext, this); |
|
215 outputRecord.initHandshaker(); |
|
216 } |
|
217 |
|
218 // kickstart the handshake if needed |
|
219 // |
|
220 // Need no kickstart message on server side unless the connection |
|
221 // has been estabilished. |
|
222 if(isNegotiated || sslConfig.isClientMode) { |
|
223 handshakeContext.kickstart(); |
|
224 } |
|
225 } |
|
226 |
|
227 void keyUpdate() throws IOException { |
|
228 // TODO: TLS 1.3 |
|
229 kickstart(); |
|
230 } |
|
231 |
|
232 // Note: close_notify is delivered as awarning alert. |
|
233 void warning(Alert alert) { |
|
234 // For initial handshaking, don't send awarning alert message to peer |
|
235 // if handshaker has not started. |
|
236 if (isNegotiated || handshakeContext != null) { |
|
237 try { |
|
238 outputRecord.encodeAlert(Alert.Level.WARNING.level, alert.id); |
|
239 } catch (IOException ioe) { |
|
240 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { |
|
241 SSLLogger.warning( |
|
242 "Warning: failed to send warning alert " + alert, ioe); |
|
243 } |
|
244 } |
|
245 } |
|
246 } |
|
247 |
|
248 void fatal(Alert alert, |
|
249 String diagnostic) throws SSLException { |
|
250 fatal(alert, diagnostic, null); |
|
251 } |
|
252 |
|
253 void fatal(Alert alert, Throwable cause) throws SSLException { |
|
254 fatal(alert, null, cause); |
|
255 } |
|
256 |
|
257 void fatal(Alert alert, |
|
258 String diagnostic, Throwable cause) throws SSLException { |
|
259 fatal(alert, diagnostic, false, cause); |
|
260 } |
|
261 |
|
262 // Note: close_notify is not delivered via fatal() methods. |
|
263 void fatal(Alert alert, String diagnostic, |
|
264 boolean recvFatalAlert, Throwable cause) throws SSLException { |
|
265 // If we've already shutdown because of an error, there is nothing we |
|
266 // can do except rethrow the exception. |
|
267 // |
|
268 // Most exceptions seen here will be SSLExceptions. We may find the |
|
269 // occasional Exception which hasn't been converted to a SSLException, |
|
270 // so we'll do it here. |
|
271 if (closeReason != null) { |
|
272 if (cause == null) { |
|
273 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { |
|
274 SSLLogger.warning( |
|
275 "Closed transport, general or untracked problem"); |
|
276 } |
|
277 throw alert.createSSLException( |
|
278 "Closed transport, general or untracked problem"); |
|
279 } |
|
280 |
|
281 if (cause instanceof SSLException) { |
|
282 throw (SSLException)cause; |
|
283 } else { // unlikely, but just in case. |
|
284 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { |
|
285 SSLLogger.warning( |
|
286 "Closed transport, rethrowing (unexpected)", cause); |
|
287 } |
|
288 throw alert.createSSLException("Unexpected rethrowing", cause); |
|
289 } |
|
290 } |
|
291 |
|
292 // If we have no further information, make a general-purpose |
|
293 // message for folks to see. We generally have one or the other. |
|
294 if (diagnostic == null) { |
|
295 if (cause == null) { |
|
296 diagnostic = "General/Untracked problem"; |
|
297 } else { |
|
298 diagnostic = cause.getMessage(); |
|
299 } |
|
300 } |
|
301 |
|
302 if (cause == null) { |
|
303 cause = alert.createSSLException(diagnostic); |
|
304 } |
|
305 |
|
306 // shutdown the transport |
|
307 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { |
|
308 SSLLogger.severe("Fatal (" + alert + "): " + diagnostic, cause); |
|
309 } |
|
310 |
|
311 // remember the close reason |
|
312 if (cause instanceof SSLException) { |
|
313 closeReason = (SSLException)cause; |
|
314 } else { |
|
315 // Including RuntimeException, but we'll throw those down below. |
|
316 closeReason = alert.createSSLException(diagnostic, cause); |
|
317 } |
|
318 |
|
319 // close inbound |
|
320 try { |
|
321 inputRecord.close(); |
|
322 } catch (IOException ioe) { |
|
323 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { |
|
324 SSLLogger.warning("Fatal: input record closure failed", ioe); |
|
325 } |
|
326 } |
|
327 |
|
328 // invalidate the session |
|
329 if (conSession != null) { |
|
330 conSession.invalidate(); |
|
331 } |
|
332 |
|
333 if (handshakeContext != null && |
|
334 handshakeContext.handshakeSession != null) { |
|
335 handshakeContext.handshakeSession.invalidate(); |
|
336 } |
|
337 |
|
338 // send fatal alert |
|
339 // |
|
340 // If we haven't even started handshaking yet, or we are the recipient |
|
341 // of a fatal alert, no need to generate a fatal close alert. |
|
342 if (!recvFatalAlert && !isOutboundDone() && !isBroken && |
|
343 (isNegotiated || handshakeContext != null)) { |
|
344 try { |
|
345 outputRecord.encodeAlert(Alert.Level.FATAL.level, alert.id); |
|
346 } catch (IOException ioe) { |
|
347 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { |
|
348 SSLLogger.warning( |
|
349 "Fatal: failed to send fatal alert " + alert, ioe); |
|
350 } |
|
351 } |
|
352 } |
|
353 |
|
354 // close outbound |
|
355 try { |
|
356 outputRecord.close(); |
|
357 } catch (IOException ioe) { |
|
358 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { |
|
359 SSLLogger.warning("Fatal: ouput record closure failed", ioe); |
|
360 } |
|
361 } |
|
362 |
|
363 // terminal handshake context |
|
364 if (handshakeContext != null) { |
|
365 handshakeContext = null; |
|
366 } |
|
367 |
|
368 // terminal the transport |
|
369 try { |
|
370 transport.shutdown(); |
|
371 } catch (IOException ioe) { |
|
372 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { |
|
373 SSLLogger.warning("Fatal: transport closure failed", ioe); |
|
374 } |
|
375 } finally { |
|
376 isBroken = true; |
|
377 } |
|
378 |
|
379 if (closeReason instanceof SSLException) { |
|
380 throw (SSLException)closeReason; |
|
381 } else { |
|
382 throw (RuntimeException)closeReason; |
|
383 } |
|
384 } |
|
385 |
|
386 void setUseClientMode(boolean useClientMode) { |
|
387 /* |
|
388 * If we need to change the client mode and the enabled |
|
389 * protocols and cipher suites haven't specifically been |
|
390 * set by the user, change them to the corresponding |
|
391 * default ones. |
|
392 */ |
|
393 if (sslConfig.isClientMode != useClientMode) { |
|
394 // Once handshaking has begun, the mode can not be reset for the |
|
395 // life of this engine. |
|
396 if (handshakeContext != null || isNegotiated) { |
|
397 throw new IllegalArgumentException( |
|
398 "Cannot change mode after SSL traffic has started"); |
|
399 } |
|
400 |
|
401 if (sslContext.isDefaultProtocolVesions( |
|
402 sslConfig.enabledProtocols)) { |
|
403 sslConfig.enabledProtocols = |
|
404 sslContext.getDefaultProtocolVersions(!useClientMode); |
|
405 } |
|
406 |
|
407 if (sslContext.isDefaultCipherSuiteList( |
|
408 sslConfig.enabledCipherSuites)) { |
|
409 sslConfig.enabledCipherSuites = |
|
410 sslContext.getDefaultCipherSuites(!useClientMode); |
|
411 } |
|
412 |
|
413 sslConfig.isClientMode = useClientMode; |
|
414 } |
|
415 |
|
416 isUnsureMode = false; |
|
417 } |
|
418 |
|
419 boolean isOutboundDone() { |
|
420 return outputRecord.isClosed(); |
|
421 } |
|
422 |
|
423 boolean isInboundDone() { |
|
424 return inputRecord.isClosed(); |
|
425 } |
|
426 |
|
427 boolean isClosed() { |
|
428 return isOutboundDone() && isInboundDone(); |
|
429 } |
|
430 |
|
431 @Override |
|
432 public void close() throws IOException { |
|
433 if (!isOutboundDone()) { |
|
434 closeOutbound(); |
|
435 } |
|
436 |
|
437 if (!isInboundDone()) { |
|
438 closeInbound(); |
|
439 } |
|
440 } |
|
441 |
|
442 void closeInbound() throws SSLException { |
|
443 if (isInboundDone()) { |
|
444 return; |
|
445 } |
|
446 |
|
447 try { |
|
448 if (isInputCloseNotified) { // passive close |
|
449 passiveInboundClose(); |
|
450 } else { // initiative close |
|
451 initiateInboundClose(); |
|
452 } |
|
453 } catch (IOException ioe) { |
|
454 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { |
|
455 SSLLogger.warning("inbound closure failed", ioe); |
|
456 } |
|
457 } |
|
458 } |
|
459 |
|
460 void closeOutbound() { |
|
461 if (isOutboundDone()) { |
|
462 return; |
|
463 } |
|
464 |
|
465 try { |
|
466 initiateOutboundClose(); |
|
467 } catch (IOException ioe) { |
|
468 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { |
|
469 SSLLogger.warning("outbound closure failed", ioe); |
|
470 } |
|
471 } |
|
472 } |
|
473 |
|
474 // Close the connection passively. The closure could be kickoff by |
|
475 // receiving a close_notify alert or reaching end_of_file of the socket. |
|
476 private void passiveInboundClose() throws IOException { |
|
477 if (!isInboundDone()) { |
|
478 inputRecord.close(); |
|
479 } |
|
480 |
|
481 // For TLS 1.3, output closure is independent from input closure. |
|
482 // if (isNegotiated && protocolVersion.useTLS13PlusSpec()) { |
|
483 // return; |
|
484 // } |
|
485 |
|
486 // For TLS 1.2 and prior version, it is required to respond with |
|
487 // a close_notify alert of its own and close down the connection |
|
488 // immediately, discarding any pending writes. |
|
489 if (!isOutboundDone() && !isOutputCloseNotified) { |
|
490 try { |
|
491 // send a close_notify alert |
|
492 warning(Alert.CLOSE_NOTIFY); |
|
493 } finally { |
|
494 // any data received after a closure alert is ignored. |
|
495 isOutputCloseNotified = true; |
|
496 outputRecord.close(); |
|
497 } |
|
498 } |
|
499 |
|
500 transport.shutdown(); |
|
501 } |
|
502 |
|
503 // Initiate a close by sending a close_notify alert. |
|
504 private void initiateInboundClose() throws IOException { |
|
505 // TLS 1.3 does not define how to initiate and close a TLS connection |
|
506 // gracefully. We will always send a close_notify alert, and close |
|
507 // the underlying transportation layer if needed. |
|
508 if (!isOutboundDone() && !isOutputCloseNotified) { |
|
509 try { |
|
510 // send a close_notify alert |
|
511 warning(Alert.CLOSE_NOTIFY); |
|
512 } finally { |
|
513 // any data received after a closure alert is ignored. |
|
514 isOutputCloseNotified = true; |
|
515 outputRecord.close(); |
|
516 } |
|
517 } |
|
518 |
|
519 // For TLS 1.3, input closure is independent from output closure. Both |
|
520 // parties need not wait to receive a "close_notify" alert before |
|
521 // closing their read side of the connection. |
|
522 // |
|
523 // For TLS 1.2 and prior version, it is not required for the initiator |
|
524 // of the close to wait for the responding close_notify alert before |
|
525 // closing the read side of the connection. |
|
526 try { |
|
527 transport.shutdown(); |
|
528 } finally { |
|
529 if (!isInboundDone()) { |
|
530 inputRecord.close(); |
|
531 } |
|
532 } |
|
533 } |
|
534 |
|
535 // Initiate a close by sending a close_notify alert. |
|
536 private void initiateOutboundClose() throws IOException { |
|
537 if (!isOutboundDone() && !isOutputCloseNotified) { |
|
538 try { // close outputRecord |
|
539 // send a close_notify alert |
|
540 warning(Alert.CLOSE_NOTIFY); |
|
541 } finally { |
|
542 // any data received after a closure alert is ignored. |
|
543 isOutputCloseNotified = true; |
|
544 outputRecord.close(); |
|
545 } |
|
546 } |
|
547 |
|
548 // For TLS 1.3, output closure is independent from input closure. |
|
549 // |
|
550 // if (isNegotiated && protocolVersion.useTLS13PlusSpec()) { |
|
551 // return; |
|
552 // } |
|
553 // |
|
554 |
|
555 // It is not required for the initiator of the close to wait for the |
|
556 // responding close_notify alert before closing the read side of the |
|
557 // connection. However, if the application protocol using TLS |
|
558 // provides that any data may be carried over the underlying transport |
|
559 // after the TLS connection is closed, the TLS implementation MUST |
|
560 // receive a "close_notify" alert before indicating end-of-data to the |
|
561 // application-layer. |
|
562 try { |
|
563 transport.shutdown(); |
|
564 } finally { |
|
565 if (!isInboundDone()) { |
|
566 inputRecord.close(); |
|
567 } |
|
568 } |
|
569 } |
|
570 |
|
571 // Note; HandshakeStatus.FINISHED status is retrieved in other places. |
|
572 HandshakeStatus getHandshakeStatus() { |
|
573 if (!outputRecord.isEmpty()) { |
|
574 // If no handshaking, special case to wrap alters. |
|
575 return HandshakeStatus.NEED_WRAP; |
|
576 } else if (handshakeContext != null) { |
|
577 if (!handshakeContext.delegatedActions.isEmpty()) { |
|
578 return HandshakeStatus.NEED_TASK; |
|
579 } else if (sslContext.isDTLS() && |
|
580 !inputRecord.isEmpty()) { |
|
581 return HandshakeStatus.NEED_UNWRAP_AGAIN; |
|
582 } else { |
|
583 return HandshakeStatus.NEED_UNWRAP; |
|
584 } |
|
585 } else if (isOutboundDone() && !isInboundDone()) { |
|
586 /* |
|
587 * Special case where we're closing, but |
|
588 * still need the close_notify before we |
|
589 * can officially be closed. |
|
590 * |
|
591 * Note isOutboundDone is taken care of by |
|
592 * hasOutboundData() above. |
|
593 */ |
|
594 return HandshakeStatus.NEED_UNWRAP; |
|
595 } |
|
596 |
|
597 return HandshakeStatus.NOT_HANDSHAKING; |
|
598 } |
|
599 |
|
600 HandshakeStatus finishHandshake() { |
|
601 if (protocolVersion.useTLS13PlusSpec()) { |
|
602 outputRecord.tc = this; |
|
603 cipherSuite = handshakeContext.negotiatedCipherSuite; |
|
604 inputRecord.readCipher.baseSecret = handshakeContext.baseReadSecret; |
|
605 outputRecord.writeCipher.baseSecret = handshakeContext.baseWriteSecret; |
|
606 } |
|
607 handshakeContext = null; |
|
608 // inputRecord and outputRecord shares the same handshakeHash |
|
609 // inputRecord.handshakeHash.finish(); |
|
610 outputRecord.handshakeHash.finish(); |
|
611 inputRecord.finishHandshake(); |
|
612 outputRecord.finishHandshake(); |
|
613 isNegotiated = true; |
|
614 |
|
615 // Tell folk about handshake completion, but do it in a separate thread. |
|
616 if (transport instanceof SSLSocket && |
|
617 sslConfig.handshakeListeners != null && |
|
618 !sslConfig.handshakeListeners.isEmpty()) { |
|
619 HandshakeCompletedEvent hce = |
|
620 new HandshakeCompletedEvent((SSLSocket)transport, conSession); |
|
621 Thread thread = new Thread( |
|
622 null, |
|
623 new NotifyHandshake(sslConfig.handshakeListeners, hce), |
|
624 "HandshakeCompletedNotify-Thread", |
|
625 0, |
|
626 false); |
|
627 thread.start(); |
|
628 } |
|
629 return HandshakeStatus.FINISHED; |
|
630 } |
|
631 |
|
632 // A separate thread is allocated to deliver handshake completion |
|
633 // events. |
|
634 private static class NotifyHandshake implements Runnable { |
|
635 private Set<Map.Entry<HandshakeCompletedListener, |
|
636 AccessControlContext>> targets; // who gets notified |
|
637 private HandshakeCompletedEvent event; // the notification |
|
638 |
|
639 NotifyHandshake( |
|
640 Map<HandshakeCompletedListener,AccessControlContext> listeners, |
|
641 HandshakeCompletedEvent event) { |
|
642 this.targets = new HashSet<>(listeners.entrySet()); // clone |
|
643 this.event = event; |
|
644 } |
|
645 |
|
646 @Override |
|
647 public void run() { |
|
648 // Don't need to synchronize, as it only runs in one thread. |
|
649 for (Map.Entry<HandshakeCompletedListener, |
|
650 AccessControlContext> entry : targets) { |
|
651 final HandshakeCompletedListener listener = entry.getKey(); |
|
652 AccessControlContext acc = entry.getValue(); |
|
653 AccessController.doPrivileged(new PrivilegedAction<Void>() { |
|
654 @Override |
|
655 public Void run() { |
|
656 listener.handshakeCompleted(event); |
|
657 return null; |
|
658 } |
|
659 }, acc); |
|
660 } |
|
661 } |
|
662 } |
|
663 } |