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