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 }