1 /*
  2  * Copyright (c) 2018, 2021, 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.IOException;
 29 import java.security.AccessControlContext;
 30 import java.security.AccessController;
 31 import java.security.PrivilegedAction;
 32 import java.util.HashMap;
 33 import java.util.HashSet;
 34 import java.util.List;
 35 import java.util.Map;
 36 import java.util.Set;
 37 import javax.net.ssl.HandshakeCompletedEvent;
 38 import javax.net.ssl.HandshakeCompletedListener;
 39 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
 40 import javax.net.ssl.SSLException;
 41 import javax.net.ssl.SSLSocket;
 42 
 43 /**
 44  * SSL/(D)TLS transportation context.
 45  */
 46 final class TransportContext implements ConnectionContext {
 47     final SSLTransport              transport;
 48 
 49     // registered plaintext consumers
 50     final Map<Byte, SSLConsumer>    consumers;
 51     @SuppressWarnings("removal")
 52     final AccessControlContext      acc;
 53 
 54     final SSLContextImpl            sslContext;
 55     final SSLConfiguration          sslConfig;
 56     final InputRecord               inputRecord;
 57     final OutputRecord              outputRecord;
 58 
 59     // connection status
 60     boolean                         isUnsureMode;
 61     boolean                         isNegotiated = false;
 62     boolean                         isBroken = false;
 63     boolean                         isInputCloseNotified = false;
 64     boolean                         peerUserCanceled = false;
 65     Exception                       closeReason = null;
 66     Exception                       delegatedThrown = null;
 67 
 68     // For TLS 1.3 full handshake, the last handshake flight could be wrapped
 69     // and encrypted in one record and delegated task would be used.  There is
 70     // no chance to return FINISHED handshake status with SSLEngine.(un)wrap().
 71     // However, per the HandshakeStatus.FINISHED specification, this value is
 72     // only generated by a call to SSLEngine.wrap()/unwrap() and it is never
 73     // generated by SSLEngine.getHandshakeStatus().
 74     //
 75     // In order to workaround this case for TLS 1.3, the FINISHED status is
 76     // present with SSLEngine.wrap() while delivering of the NewSessionTicket
 77     // post-handshake message.  If this post-handshake message is not needed,
 78     // a follow-on SSLEngine.wrap() should be called to indicate the FINISHED
 79     // handshake status.  Although this special SSLEngine.wrap() should not
 80     // consume or produce any application or network data.
 81     boolean                         needHandshakeFinishedStatus = false;
 82     boolean                         hasDelegatedFinished = false;
 83 
 84     // negotiated security parameters
 85     SSLSessionImpl                  conSession;
 86     ProtocolVersion                 protocolVersion;
 87     String                          applicationProtocol= null;
 88 
 89     // handshake context
 90     HandshakeContext                handshakeContext = null;
 91 
 92     // connection reserved status for handshake.
 93     boolean                         secureRenegotiation = false;
 94     byte[]                          clientVerifyData;
 95     byte[]                          serverVerifyData;
 96 
 97     // connection sensitive configuration
 98     List<NamedGroup>                serverRequestedNamedGroups;
 99 
100     CipherSuite cipherSuite;
101     private static final byte[] emptyByteArray = new byte[0];
102 
103     // Please never use the transport parameter other than storing a
104     // reference to this object.
105     //
106     // Called by SSLEngineImpl
107     TransportContext(SSLContextImpl sslContext, SSLTransport transport,
108             InputRecord inputRecord, OutputRecord outputRecord) {
109         this(sslContext, transport, new SSLConfiguration(sslContext, false),
110                 inputRecord, outputRecord, true);
111     }
112 
113     // Please never use the transport parameter other than storing a
114     // reference to this object.
115     //
116     // Called by SSLSocketImpl
117     TransportContext(SSLContextImpl sslContext, SSLTransport transport,
118             InputRecord inputRecord, OutputRecord outputRecord,
119             boolean isClientMode) {
120         this(sslContext, transport,
121                 new SSLConfiguration(sslContext, isClientMode),
122                 inputRecord, outputRecord, false);
123     }
124 
125     // Please never use the transport parameter other than storing a
126     // reference to this object.
127     //
128     // Called by SSLSocketImpl with an existing SSLConfig
129     TransportContext(SSLContextImpl sslContext, SSLTransport transport,
130             SSLConfiguration sslConfig,
131             InputRecord inputRecord, OutputRecord outputRecord) {
132         this(sslContext, transport, (SSLConfiguration)sslConfig.clone(),
133                 inputRecord, outputRecord, false);
134     }
135 
136     @SuppressWarnings("removal")
137     private TransportContext(SSLContextImpl sslContext, SSLTransport transport,
138             SSLConfiguration sslConfig, InputRecord inputRecord,
139             OutputRecord outputRecord, boolean isUnsureMode) {
140         this.transport = transport;
141         this.sslContext = sslContext;
142         this.inputRecord = inputRecord;
143         this.outputRecord = outputRecord;
144         this.sslConfig = sslConfig;
145         if (this.sslConfig.maximumPacketSize == 0) {
146             this.sslConfig.maximumPacketSize = outputRecord.getMaxPacketSize();
147         }
148         this.isUnsureMode = isUnsureMode;
149 
150         // initial security parameters
151         this.conSession = new SSLSessionImpl();
152         this.protocolVersion = this.sslConfig.maximumProtocolVersion;
153         this.clientVerifyData = emptyByteArray;
154         this.serverVerifyData = emptyByteArray;
155 
156         this.acc = AccessController.getContext();
157         this.consumers = new HashMap<>();
158     }
159 
160     // Dispatch plaintext to a specific consumer.
161     void dispatch(Plaintext plaintext) throws IOException {
162         if (plaintext == null) {
163             return;
164         }
165 
166         ContentType ct = ContentType.valueOf(plaintext.contentType);
167         if (ct == null) {
168             throw fatal(Alert.UNEXPECTED_MESSAGE,
169                 "Unknown content type: " + plaintext.contentType);
170         }
171 
172         switch (ct) {
173             case HANDSHAKE:
174                 byte type = HandshakeContext.getHandshakeType(this,
175                         plaintext);
176                 if (handshakeContext == null) {
177                     if (type == SSLHandshake.KEY_UPDATE.id ||
178                             type == SSLHandshake.NEW_SESSION_TICKET.id) {
179                         if (!isNegotiated) {
180                             throw fatal(Alert.UNEXPECTED_MESSAGE,
181                                     "Unexpected unnegotiated post-handshake" +
182                                             " message: " +
183                                             SSLHandshake.nameOf(type));
184                         }
185 
186                         if (!PostHandshakeContext.isConsumable(this, type)) {
187                             throw fatal(Alert.UNEXPECTED_MESSAGE,
188                                     "Unexpected post-handshake message: " +
189                                     SSLHandshake.nameOf(type));
190                         }
191 
192                         handshakeContext = new PostHandshakeContext(this);
193                     } else {
194                         handshakeContext = sslConfig.isClientMode ?
195                                 new ClientHandshakeContext(sslContext, this) :
196                                 new ServerHandshakeContext(sslContext, this);
197                         outputRecord.initHandshaker();
198                     }
199                 }
200                 handshakeContext.dispatch(type, plaintext);
201                 break;
202             case ALERT:
203                 Alert.alertConsumer.consume(this, plaintext.fragment);
204                 break;
205             default:
206                 SSLConsumer consumer = consumers.get(plaintext.contentType);
207                 if (consumer != null) {
208                     consumer.consume(this, plaintext.fragment);
209                 } else {
210                     throw fatal(Alert.UNEXPECTED_MESSAGE,
211                         "Unexpected content: " + plaintext.contentType);
212                 }
213         }
214     }
215 
216     void kickstart() throws IOException {
217         if (isUnsureMode) {
218             throw new IllegalStateException("Client/Server mode not yet set.");
219         }
220 
221         if (outputRecord.isClosed() || inputRecord.isClosed() || isBroken) {
222             if (closeReason != null) {
223                 throw new SSLException(
224                         "Cannot kickstart, the connection is broken or closed",
225                         closeReason);
226             } else {
227                 throw new SSLException(
228                         "Cannot kickstart, the connection is broken or closed");
229             }
230         }
231 
232         // initialize the handshaker if necessary
233         if (handshakeContext == null) {
234             //  TLS1.3 post-handshake
235             if (isNegotiated && protocolVersion.useTLS13PlusSpec()) {
236                 handshakeContext = new PostHandshakeContext(this);
237             } else {
238                 handshakeContext = sslConfig.isClientMode ?
239                         new ClientHandshakeContext(sslContext, this) :
240                         new ServerHandshakeContext(sslContext, this);
241                 outputRecord.initHandshaker();
242             }
243         }
244 
245         // kickstart the handshake if needed
246         //
247         // Need no kickstart message on server side unless the connection
248         // has been established.
249         if(isNegotiated || sslConfig.isClientMode) {
250            handshakeContext.kickstart();
251         }
252     }
253 
254     boolean isPostHandshakeContext() {
255         return handshakeContext != null &&
256                 (handshakeContext instanceof PostHandshakeContext);
257     }
258 
259     // Note: Don't use this method for close_nofity, use closeNotify() instead.
260     void warning(Alert alert) {
261         // For initial handshaking, don't send a warning alert message to peer
262         // if handshaker has not started.
263         if (isNegotiated || handshakeContext != null) {
264             try {
265                 outputRecord.encodeAlert(Alert.Level.WARNING.level, alert.id);
266             } catch (IOException ioe) {
267                 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
268                     SSLLogger.warning(
269                         "Warning: failed to send warning alert " + alert, ioe);
270                 }
271             }
272         }
273     }
274 
275     // Note: close_notify is delivered as a warning alert.
276     void closeNotify(boolean isUserCanceled) throws IOException {
277         // Socket transport is special because of the SO_LINGER impact.
278         if (transport instanceof SSLSocketImpl) {
279             ((SSLSocketImpl)transport).closeNotify(isUserCanceled);
280         } else {
281             // Need a lock here so that the user_canceled alert and the
282             // close_notify alert can be delivered together.
283             outputRecord.recordLock.lock();
284             try {
285                 try {
286                     // send a user_canceled alert if needed.
287                     if (isUserCanceled) {
288                         warning(Alert.USER_CANCELED);
289                     }
290 
291                     // send a close_notify alert
292                     warning(Alert.CLOSE_NOTIFY);
293                 } finally {
294                     outputRecord.close();
295                 }
296             } finally {
297                 outputRecord.recordLock.unlock();
298             }
299         }
300     }
301 
302     SSLException fatal(Alert alert,
303             String diagnostic) throws SSLException {
304         return fatal(alert, diagnostic, null);
305     }
306 
307     SSLException fatal(Alert alert, Throwable cause) throws SSLException {
308         return fatal(alert, null, cause);
309     }
310 
311     SSLException fatal(Alert alert,
312             String diagnostic, Throwable cause) throws SSLException {
313         return fatal(alert, diagnostic, false, cause);
314     }
315 
316     // Note: close_notify is not delivered via fatal() methods.
317     SSLException fatal(Alert alert, String diagnostic,
318             boolean recvFatalAlert, Throwable cause) throws SSLException {
319         // If we've already shutdown because of an error, there is nothing we
320         // can do except rethrow the exception.
321         //
322         // Most exceptions seen here will be SSLExceptions. We may find the
323         // occasional Exception which hasn't been converted to a SSLException,
324         // so we'll do it here.
325         if (closeReason != null) {
326             if (cause == null) {
327                 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
328                     SSLLogger.warning(
329                             "Closed transport, general or untracked problem");
330                 }
331                 throw alert.createSSLException(
332                         "Closed transport, general or untracked problem");
333             }
334 
335             if (cause instanceof SSLException) {
336                 throw (SSLException)cause;
337             } else {    // unlikely, but just in case.
338                 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
339                     SSLLogger.warning(
340                             "Closed transport, unexpected rethrowing", cause);
341                 }
342                 throw alert.createSSLException("Unexpected rethrowing", cause);
343             }
344         }
345 
346         // If we have no further information, make a general-purpose
347         // message for folks to see.  We generally have one or the other.
348         if (diagnostic == null) {
349             if (cause == null) {
350                 diagnostic = "General/Untracked problem";
351             } else {
352                 diagnostic = cause.getMessage();
353             }
354         }
355 
356         if (cause == null) {
357             cause = alert.createSSLException(diagnostic);
358         }
359 
360         // shutdown the transport
361         if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
362             SSLLogger.severe("Fatal (" + alert + "): " + diagnostic, cause);
363         }
364 
365         // remember the close reason
366         if (cause instanceof SSLException) {
367             closeReason = (SSLException)cause;
368         } else {
369             // Including RuntimeException, but we'll throw those down below.
370             closeReason = alert.createSSLException(diagnostic, cause);
371         }
372 
373         // close inbound
374         try {
375             inputRecord.close();
376         } catch (IOException ioe) {
377             if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
378                 SSLLogger.warning("Fatal: input record closure failed", ioe);
379             }
380 
381             closeReason.addSuppressed(ioe);
382         }
383 
384         // invalidate the session
385         if (conSession != null) {
386             conSession.invalidate();
387         }
388 
389         if (handshakeContext != null &&
390                 handshakeContext.handshakeSession != null) {
391             handshakeContext.handshakeSession.invalidate();
392         }
393 
394         // send fatal alert
395         //
396         // If we haven't even started handshaking yet, or we are the recipient
397         // of a fatal alert, no need to generate a fatal close alert.
398         if (!recvFatalAlert && !isOutboundClosed() && !isBroken &&
399                 (isNegotiated || handshakeContext != null)) {
400             try {
401                 outputRecord.encodeAlert(Alert.Level.FATAL.level, alert.id);
402             } catch (IOException ioe) {
403                 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
404                     SSLLogger.warning(
405                         "Fatal: failed to send fatal alert " + alert, ioe);
406                 }
407 
408                 closeReason.addSuppressed(ioe);
409             }
410         }
411 
412         // close outbound
413         try {
414             outputRecord.close();
415         } catch (IOException ioe) {
416             if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
417                 SSLLogger.warning("Fatal: output record closure failed", ioe);
418             }
419 
420             closeReason.addSuppressed(ioe);
421         }
422 
423         // terminate the handshake context
424         if (handshakeContext != null) {
425             handshakeContext = null;
426         }
427 
428         // terminate the transport
429         try {
430             transport.shutdown();
431         } catch (IOException ioe) {
432             if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
433                 SSLLogger.warning("Fatal: transport closure failed", ioe);
434             }
435 
436             closeReason.addSuppressed(ioe);
437         } finally {
438             isBroken = true;
439         }
440 
441         if (closeReason instanceof SSLException) {
442             throw (SSLException)closeReason;
443         } else {
444             throw (RuntimeException)closeReason;
445         }
446     }
447 
448     void setUseClientMode(boolean useClientMode) {
449         // Once handshaking has begun, the mode can not be reset for the
450         // life of this engine.
451         if (handshakeContext != null || isNegotiated) {
452             throw new IllegalArgumentException(
453                     "Cannot change mode after SSL traffic has started");
454         }
455 
456         /*
457          * If we need to change the client mode and the enabled
458          * protocols and cipher suites haven't specifically been
459          * set by the user, change them to the corresponding
460          * default ones.
461          */
462         if (sslConfig.isClientMode != useClientMode) {
463             if (sslContext.isDefaultProtocolVesions(
464                     sslConfig.enabledProtocols)) {
465                 sslConfig.enabledProtocols =
466                         sslContext.getDefaultProtocolVersions(!useClientMode);
467             }
468 
469             if (sslContext.isDefaultCipherSuiteList(
470                     sslConfig.enabledCipherSuites)) {
471                 sslConfig.enabledCipherSuites =
472                         sslContext.getDefaultCipherSuites(!useClientMode);
473             }
474 
475             sslConfig.toggleClientMode();
476         }
477 
478         isUnsureMode = false;
479     }
480 
481     // The OutputRecord is closed and not buffered output record.
482     boolean isOutboundDone() {
483         return outputRecord.isClosed() && outputRecord.isEmpty();
484     }
485 
486     // The OutputRecord is closed, but buffered output record may be still
487     // waiting for delivery to the underlying connection.
488     boolean isOutboundClosed() {
489         return outputRecord.isClosed();
490     }
491 
492     boolean isInboundClosed() {
493         return inputRecord.isClosed();
494     }
495 
496     // Close inbound, no more data should be delivered to the underlying
497     // transportation connection.
498     void closeInbound() throws SSLException {
499         if (isInboundClosed()) {
500             return;
501         }
502 
503         try {
504             // Important note: check if the initial handshake is started at
505             // first so that the passiveInboundClose() implementation need not
506             // to consider the case any more.
507             if (!isInputCloseNotified) {
508                 // the initial handshake is not started
509                 initiateInboundClose();
510             } else {
511                 passiveInboundClose();
512             }
513         } catch (IOException ioe) {
514             if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
515                 SSLLogger.warning("inbound closure failed", ioe);
516             }
517         }
518     }
519 
520     // Close the connection passively.  The closure could be kickoff by
521     // receiving a close_notify alert or reaching end_of_file of the socket.
522     //
523     // Note that this method is called only if the initial handshake has
524     // started or completed.
525     private void passiveInboundClose() throws IOException {
526         if (!isInboundClosed()) {
527             inputRecord.close();
528         }
529 
530         // For TLS 1.2 and prior version, it is required to respond with
531         // a close_notify alert of its own and close down the connection
532         // immediately, discarding any pending writes.
533         if (!isOutboundClosed()) {
534             boolean needCloseNotify = SSLConfiguration.acknowledgeCloseNotify;
535             if (!needCloseNotify) {
536                 if (isNegotiated) {
537                     if (!protocolVersion.useTLS13PlusSpec()) {
538                         needCloseNotify = true;
539                     }
540                 } else if (handshakeContext != null) {  // initial handshake
541                     ProtocolVersion pv = handshakeContext.negotiatedProtocol;
542                     if (pv == null || (!pv.useTLS13PlusSpec())) {
543                         needCloseNotify = true;
544                     }
545                 }
546             }
547 
548             if (needCloseNotify) {
549                 closeNotify(false);
550             }
551         }
552     }
553 
554     // Initiate a inbound close when the handshake is not started.
555     private void initiateInboundClose() throws IOException {
556         if (!isInboundClosed()) {
557             inputRecord.close();
558         }
559     }
560 
561     // Close outbound, no more data should be received from the underlying
562     // transportation connection.
563     void closeOutbound() {
564         if (isOutboundClosed()) {
565             return;
566         }
567 
568         try {
569              initiateOutboundClose();
570         } catch (IOException ioe) {
571             if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
572                 SSLLogger.warning("outbound closure failed", ioe);
573             }
574         }
575     }
576 
577     // Initiate a close by sending a close_notify alert.
578     private void initiateOutboundClose() throws IOException {
579         boolean useUserCanceled = false;
580         if (!isNegotiated && (handshakeContext != null) && !peerUserCanceled) {
581             // initial handshake
582             useUserCanceled = true;
583         }
584 
585         closeNotify(useUserCanceled);
586     }
587 
588     // Note: HandshakeStatus.FINISHED status is retrieved in other places.
589     HandshakeStatus getHandshakeStatus() {
590         if (!outputRecord.isEmpty()) {
591             // If not handshaking, special case to wrap alerts or
592             // post-handshake messages.
593             return HandshakeStatus.NEED_WRAP;
594         } else if (isOutboundClosed() && isInboundClosed()) {
595             return HandshakeStatus.NOT_HANDSHAKING;
596         } else if (handshakeContext != null) {
597             if (!handshakeContext.delegatedActions.isEmpty()) {
598                 return HandshakeStatus.NEED_TASK;
599             } else if (!isInboundClosed()) {
600                 if (sslContext.isDTLS() && !inputRecord.isEmpty()) {
601                     return HandshakeStatus.NEED_UNWRAP_AGAIN;
602                 } else {
603                     return HandshakeStatus.NEED_UNWRAP;
604                 }
605             } else if (!isOutboundClosed()) {
606                 // Special case that the inbound was closed, but outbound open.
607                 return HandshakeStatus.NEED_WRAP;
608             }   // Otherwise, both inbound and outbound are closed.
609         } else if (needHandshakeFinishedStatus) {
610             // Special case to get FINISHED status for TLS 1.3 full handshake.
611             return HandshakeStatus.NEED_WRAP;
612         }
613 
614         return HandshakeStatus.NOT_HANDSHAKING;
615     }
616 
617     HandshakeStatus finishHandshake() {
618         if (protocolVersion.useTLS13PlusSpec()) {
619             outputRecord.tc = this;
620             inputRecord.tc = this;
621             cipherSuite = handshakeContext.negotiatedCipherSuite;
622             inputRecord.readCipher.baseSecret =
623                     handshakeContext.baseReadSecret;
624             outputRecord.writeCipher.baseSecret =
625                     handshakeContext.baseWriteSecret;
626         }
627 
628         handshakeContext = null;
629         outputRecord.handshakeHash.finish();
630         inputRecord.finishHandshake();
631         outputRecord.finishHandshake();
632         isNegotiated = true;
633 
634         // Tell folk about handshake completion, but do it in a separate thread.
635         if (transport instanceof SSLSocket &&
636                 sslConfig.handshakeListeners != null &&
637                 !sslConfig.handshakeListeners.isEmpty()) {
638             HandshakeCompletedEvent hce =
639                 new HandshakeCompletedEvent((SSLSocket)transport, conSession);
640             Thread.ofVirtual()
641                     .name("HandshakeCompletedNotify-Thread")
642                     .inheritInheritableThreadLocals(false)
643                     .start(new NotifyHandshake(sslConfig.handshakeListeners, hce));
644         }
645 
646         return HandshakeStatus.FINISHED;
647     }
648 
649     HandshakeStatus finishPostHandshake() {
650         handshakeContext = null;
651 
652         // Note: May need trigger handshake completion even for post-handshake
653         // authentication in the future.
654 
655         return HandshakeStatus.FINISHED;
656     }
657 
658     // A separate thread is allocated to deliver handshake completion
659     // events.
660     private static class NotifyHandshake implements Runnable {
661         @SuppressWarnings("removal")
662         private final Set<Map.Entry<HandshakeCompletedListener,
663                 AccessControlContext>> targets;         // who gets notified
664         private final HandshakeCompletedEvent event;    // the notification
665 
666         NotifyHandshake(
667                 @SuppressWarnings("removal")
668                 Map<HandshakeCompletedListener,AccessControlContext> listeners,
669                 HandshakeCompletedEvent event) {
670             this.targets = new HashSet<>(listeners.entrySet());     // clone
671             this.event = event;
672         }
673 
674         @SuppressWarnings("removal")
675         @Override
676         public void run() {
677             // Don't need to synchronize, as it only runs in one thread.
678             for (Map.Entry<HandshakeCompletedListener,
679                     AccessControlContext> entry : targets) {
680                 final HandshakeCompletedListener listener = entry.getKey();
681                 AccessControlContext acc = entry.getValue();
682                 AccessController.doPrivileged(new PrivilegedAction<Void>() {
683                     @Override
684                     public Void run() {
685                         listener.handshakeCompleted(event);
686                         return null;
687                     }
688                 }, acc);
689             }
690         }
691     }
692 }