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