1 /* 2 * Copyright 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.conscrypt; 18 19 import static javax.net.ssl.SSLEngineResult.Status.OK; 20 import static org.conscrypt.SSLUtils.EngineStates.STATE_CLOSED; 21 import static org.conscrypt.SSLUtils.EngineStates.STATE_HANDSHAKE_COMPLETED; 22 import static org.conscrypt.SSLUtils.EngineStates.STATE_HANDSHAKE_STARTED; 23 import static org.conscrypt.SSLUtils.EngineStates.STATE_NEW; 24 import static org.conscrypt.SSLUtils.EngineStates.STATE_READY; 25 import static org.conscrypt.SSLUtils.EngineStates.STATE_READY_HANDSHAKE_CUT_THROUGH; 26 27 import java.io.EOFException; 28 import java.io.IOException; 29 import java.io.InputStream; 30 import java.io.OutputStream; 31 import java.net.InetAddress; 32 import java.net.Socket; 33 import java.net.SocketException; 34 import java.nio.ByteBuffer; 35 import java.security.PrivateKey; 36 import javax.net.ssl.SSLEngineResult; 37 import javax.net.ssl.SSLEngineResult.HandshakeStatus; 38 import javax.net.ssl.SSLException; 39 import javax.net.ssl.SSLParameters; 40 import javax.net.ssl.SSLSession; 41 42 /** 43 * Implements crypto handling by delegating to {@link ConscryptEngine}. 44 */ 45 class ConscryptEngineSocket extends OpenSSLSocketImpl { 46 private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0); 47 48 private final ConscryptEngine engine; 49 private final Object stateLock = new Object(); 50 private final Object handshakeLock = new Object(); 51 52 private SSLOutputStream out; 53 private SSLInputStream in; 54 55 // @GuardedBy("stateLock"); 56 private int state = STATE_NEW; 57 58 // The constructors should not be called except from the Platform class, because we may 59 // want to construct a subclass instead. 60 ConscryptEngineSocket(SSLParametersImpl sslParameters) throws IOException { 61 engine = newEngine(sslParameters, this); 62 } 63 64 ConscryptEngineSocket(String hostname, int port, SSLParametersImpl sslParameters) 65 throws IOException { 66 super(hostname, port); 67 engine = newEngine(sslParameters, this); 68 } 69 70 ConscryptEngineSocket(InetAddress address, int port, SSLParametersImpl sslParameters) 71 throws IOException { 72 super(address, port); 73 engine = newEngine(sslParameters, this); 74 } 75 76 ConscryptEngineSocket(String hostname, int port, InetAddress clientAddress, int clientPort, 77 SSLParametersImpl sslParameters) throws IOException { 78 super(hostname, port, clientAddress, clientPort); 79 engine = newEngine(sslParameters, this); 80 } 81 82 ConscryptEngineSocket(InetAddress address, int port, InetAddress clientAddress, int clientPort, 83 SSLParametersImpl sslParameters) throws IOException { 84 super(address, port, clientAddress, clientPort); 85 engine = newEngine(sslParameters, this); 86 } 87 88 ConscryptEngineSocket(Socket socket, String hostname, int port, boolean autoClose, 89 SSLParametersImpl sslParameters) throws IOException { 90 super(socket, hostname, port, autoClose); 91 engine = newEngine(sslParameters, this); 92 } 93 94 private static ConscryptEngine newEngine( 95 SSLParametersImpl sslParameters, final ConscryptEngineSocket socket) { 96 ConscryptEngine engine = new ConscryptEngine(sslParameters, socket.peerInfoProvider()); 97 98 // When the handshake completes, notify any listeners. 99 engine.setHandshakeListener(new HandshakeListener() { 100 /** 101 * Protected by {@code stateLock} 102 */ 103 @Override 104 public void onHandshakeFinished() { 105 // Just call the outer class method. 106 socket.onHandshakeFinished(); 107 } 108 }); 109 110 // Transition the engine state to MODE_SET 111 engine.setUseClientMode(sslParameters.getUseClientMode()); 112 return engine; 113 } 114 115 @Override 116 public final SSLParameters getSSLParameters() { 117 return engine.getSSLParameters(); 118 } 119 120 @Override 121 public final void setSSLParameters(SSLParameters sslParameters) { 122 engine.setSSLParameters(sslParameters); 123 } 124 125 @Override 126 public final void startHandshake() throws IOException { 127 checkOpen(); 128 129 try { 130 synchronized (handshakeLock) { 131 // Only lock stateLock when we begin the handshake. This is done so that we don't 132 // hold the stateLock when we invoke the handshake completion listeners. 133 synchronized (stateLock) { 134 // Initialize the handshake if we haven't already. 135 if (state == STATE_NEW) { 136 state = STATE_HANDSHAKE_STARTED; 137 engine.beginHandshake(); 138 in = new SSLInputStream(); 139 out = new SSLOutputStream(); 140 } else { 141 // We've either started the handshake already or have been closed. 142 // Do nothing in both cases. 143 // 144 // NOTE: BoringSSL does not support initiating renegotiation, so we always 145 // ignore addition handshake calls. 146 return; 147 } 148 } 149 150 doHandshake(); 151 } 152 } catch (SSLException e) { 153 close(); 154 throw e; 155 } catch (IOException e) { 156 close(); 157 throw e; 158 } catch (Exception e) { 159 close(); 160 // Convert anything else to a handshake exception. 161 throw SSLUtils.toSSLHandshakeException(e); 162 } 163 } 164 165 private void doHandshake() throws IOException { 166 try { 167 boolean finished = false; 168 while (!finished) { 169 switch (engine.getHandshakeStatus()) { 170 case NEED_UNWRAP: 171 if (in.readInternal(EmptyArray.BYTE, 0, 0) < 0) { 172 // Can't complete the handshake due to EOF. 173 throw SSLUtils.toSSLHandshakeException(new EOFException()); 174 } 175 break; 176 case NEED_WRAP: { 177 out.writeInternal(EMPTY_BUFFER); 178 // Always flush handshake frames immediately. 179 out.flushInternal(); 180 break; 181 } 182 case NEED_TASK: { 183 // Should never get here, since our engine never provides tasks. 184 throw new IllegalStateException("Engine tasks are unsupported"); 185 } 186 case NOT_HANDSHAKING: 187 case FINISHED: { 188 // Handshake is complete. 189 finished = true; 190 break; 191 } 192 default: { 193 throw new IllegalStateException( 194 "Unknown handshake status: " + engine.getHandshakeStatus()); 195 } 196 } 197 } 198 } catch (SSLException e) { 199 close(); 200 throw e; 201 } catch (IOException e) { 202 close(); 203 throw e; 204 } catch (Exception e) { 205 close(); 206 // Convert anything else to a handshake exception. 207 throw SSLUtils.toSSLHandshakeException(e); 208 } 209 } 210 211 @Override 212 public final InputStream getInputStream() throws IOException { 213 checkOpen(); 214 215 // Block waiting for a handshake without a lock held. It's possible that the socket 216 // is closed at this point. If that happens, we'll still return the input stream but 217 // all reads on it will throw. 218 waitForHandshake(); 219 return in; 220 } 221 222 @Override 223 public final OutputStream getOutputStream() throws IOException { 224 checkOpen(); 225 226 // Block waiting for a handshake without a lock held. It's possible that the socket 227 // is closed at this point. If that happens, we'll still return the input stream but 228 // all reads on it will throw. 229 waitForHandshake(); 230 231 return out; 232 } 233 234 @Override 235 public final SSLSession getHandshakeSession() { 236 return engine.handshakeSession(); 237 } 238 239 @Override 240 public final SSLSession getSession() { 241 SSLSession session = engine.getSession(); 242 if (SSLNullSession.isNullSession(session)) { 243 boolean handshakeCompleted = false; 244 try { 245 if (isConnected()) { 246 waitForHandshake(); 247 handshakeCompleted = true; 248 } 249 } catch (IOException e) { 250 // Fall through. 251 } 252 253 if (!handshakeCompleted) { 254 // Return an invalid session with invalid cipher suite of "SSL_NULL_WITH_NULL_NULL" 255 return session; 256 } 257 session = engine.getSession(); 258 } 259 return session; 260 } 261 262 @Override 263 final SSLSession getActiveSession() { 264 return engine.getSession(); 265 } 266 267 @Override 268 public final boolean getEnableSessionCreation() { 269 return engine.getEnableSessionCreation(); 270 } 271 272 @Override 273 public final void setEnableSessionCreation(boolean flag) { 274 engine.setEnableSessionCreation(flag); 275 } 276 277 @Override 278 public final String[] getSupportedCipherSuites() { 279 return engine.getSupportedCipherSuites(); 280 } 281 282 @Override 283 public final String[] getEnabledCipherSuites() { 284 return engine.getEnabledCipherSuites(); 285 } 286 287 @Override 288 public final void setEnabledCipherSuites(String[] suites) { 289 engine.setEnabledCipherSuites(suites); 290 } 291 292 @Override 293 public final String[] getSupportedProtocols() { 294 return engine.getSupportedProtocols(); 295 } 296 297 @Override 298 public final String[] getEnabledProtocols() { 299 return engine.getEnabledProtocols(); 300 } 301 302 @Override 303 public final void setEnabledProtocols(String[] protocols) { 304 engine.setEnabledProtocols(protocols); 305 } 306 307 /** 308 * This method enables Server Name Indication 309 * 310 * @param hostname the desired SNI hostname, or null to disable 311 */ 312 @Override 313 public final void setHostname(String hostname) { 314 engine.setHostname(hostname); 315 super.setHostname(hostname); 316 } 317 318 @Override 319 public final void setUseSessionTickets(boolean useSessionTickets) { 320 engine.setUseSessionTickets(useSessionTickets); 321 } 322 323 @Override 324 public final void setChannelIdEnabled(boolean enabled) { 325 engine.setChannelIdEnabled(enabled); 326 } 327 328 @Override 329 public final byte[] getChannelId() throws SSLException { 330 return engine.getChannelId(); 331 } 332 333 @Override 334 public final void setChannelIdPrivateKey(PrivateKey privateKey) { 335 engine.setChannelIdPrivateKey(privateKey); 336 } 337 338 @Override 339 byte[] getTlsUnique() { 340 return engine.getTlsUnique(); 341 } 342 343 @Override 344 public final boolean getUseClientMode() { 345 return engine.getUseClientMode(); 346 } 347 348 @Override 349 public final void setUseClientMode(boolean mode) { 350 engine.setUseClientMode(mode); 351 } 352 353 @Override 354 public final boolean getWantClientAuth() { 355 return engine.getWantClientAuth(); 356 } 357 358 @Override 359 public final boolean getNeedClientAuth() { 360 return engine.getNeedClientAuth(); 361 } 362 363 @Override 364 public final void setNeedClientAuth(boolean need) { 365 engine.setNeedClientAuth(need); 366 } 367 368 @Override 369 public final void setWantClientAuth(boolean want) { 370 engine.setWantClientAuth(want); 371 } 372 373 @Override 374 @SuppressWarnings("UnsynchronizedOverridesSynchronized") 375 public final void close() throws IOException { 376 // TODO: Close SSL sockets using a background thread so they close gracefully. 377 378 if (stateLock == null) { 379 // close() has been called before we've initialized the socket, so just 380 // return. 381 return; 382 } 383 384 synchronized (stateLock) { 385 if (state == STATE_CLOSED) { 386 // close() has already been called, so do nothing and return. 387 return; 388 } 389 390 state = STATE_CLOSED; 391 392 stateLock.notifyAll(); 393 } 394 395 // Close the underlying socket. 396 super.close(); 397 398 // Close the engine. 399 engine.closeInbound(); 400 engine.closeOutbound(); 401 } 402 403 @Override 404 final void setApplicationProtocols(String[] protocols) { 405 engine.setApplicationProtocols(protocols); 406 } 407 408 @Override 409 final String[] getApplicationProtocols() { 410 return engine.getApplicationProtocols(); 411 } 412 413 @Override 414 public final String getApplicationProtocol() { 415 return engine.getApplicationProtocol(); 416 } 417 418 @Override 419 public final String getHandshakeApplicationProtocol() { 420 return engine.getHandshakeApplicationProtocol(); 421 } 422 423 @Override 424 public final void setApplicationProtocolSelector(ApplicationProtocolSelector selector) { 425 setApplicationProtocolSelector( 426 selector == null ? null : new ApplicationProtocolSelectorAdapter(this, selector)); 427 } 428 429 @Override 430 final void setApplicationProtocolSelector(ApplicationProtocolSelectorAdapter selector) { 431 engine.setApplicationProtocolSelector(selector); 432 } 433 434 private void onHandshakeFinished() { 435 boolean notify = false; 436 synchronized (stateLock) { 437 if (state != STATE_CLOSED) { 438 if (state == STATE_HANDSHAKE_STARTED) { 439 state = STATE_READY_HANDSHAKE_CUT_THROUGH; 440 } else if (state == STATE_HANDSHAKE_COMPLETED) { 441 state = STATE_READY; 442 } 443 444 // Unblock threads that are waiting for our state to transition 445 // into STATE_READY or STATE_READY_HANDSHAKE_CUT_THROUGH. 446 stateLock.notifyAll(); 447 notify = true; 448 } 449 } 450 451 if (notify) { 452 notifyHandshakeCompletedListeners(); 453 } 454 } 455 456 /** 457 * Waits for the handshake to complete. 458 */ 459 private void waitForHandshake() throws IOException { 460 startHandshake(); 461 462 synchronized (stateLock) { 463 while (state != STATE_READY && state != STATE_READY_HANDSHAKE_CUT_THROUGH 464 && state != STATE_CLOSED) { 465 try { 466 stateLock.wait(); 467 } catch (InterruptedException e) { 468 Thread.currentThread().interrupt(); 469 throw new IOException("Interrupted waiting for handshake", e); 470 } 471 } 472 473 if (state == STATE_CLOSED) { 474 throw new SocketException("Socket is closed"); 475 } 476 } 477 } 478 479 private OutputStream getUnderlyingOutputStream() throws IOException { 480 return super.getOutputStream(); 481 } 482 483 private InputStream getUnderlyingInputStream() throws IOException { 484 return super.getInputStream(); 485 } 486 487 /** 488 * Wrap bytes written to the underlying socket. 489 */ 490 private final class SSLOutputStream extends OutputStream { 491 private final Object writeLock = new Object(); 492 private final ByteBuffer target; 493 private final int targetArrayOffset; 494 private OutputStream socketOutputStream; 495 496 SSLOutputStream() { 497 target = ByteBuffer.allocate(engine.getSession().getPacketBufferSize()); 498 targetArrayOffset = target.arrayOffset(); 499 } 500 501 @Override 502 public void close() throws IOException { 503 ConscryptEngineSocket.this.close(); 504 } 505 506 @Override 507 public void write(int b) throws IOException { 508 startHandshake(); 509 synchronized (writeLock) { 510 write(new byte[] {(byte) b}); 511 } 512 } 513 514 @Override 515 public void write(byte[] b) throws IOException { 516 startHandshake(); 517 synchronized (writeLock) { 518 writeInternal(ByteBuffer.wrap(b)); 519 } 520 } 521 522 @Override 523 public void write(byte[] b, int off, int len) throws IOException { 524 startHandshake(); 525 synchronized (writeLock) { 526 writeInternal(ByteBuffer.wrap(b, off, len)); 527 } 528 } 529 530 private void writeInternal(ByteBuffer buffer) throws IOException { 531 Platform.blockGuardOnNetwork(); 532 checkOpen(); 533 init(); 534 535 // Need to loop through at least once to enable handshaking where no application 536 // bytes are 537 // processed. 538 int len = buffer.remaining(); 539 SSLEngineResult engineResult; 540 do { 541 target.clear(); 542 engineResult = engine.wrap(buffer, target); 543 if (engineResult.getStatus() != OK) { 544 throw new SSLException("Unexpected engine result " + engineResult.getStatus()); 545 } 546 if (target.position() != engineResult.bytesProduced()) { 547 throw new SSLException("Engine bytesProduced " + engineResult.bytesProduced() 548 + " does not match bytes written " + target.position()); 549 } 550 len -= engineResult.bytesConsumed(); 551 if (len != buffer.remaining()) { 552 throw new SSLException("Engine did not read the correct number of bytes"); 553 } 554 555 target.flip(); 556 557 // Write the data to the socket. 558 writeToSocket(); 559 } while (len > 0); 560 } 561 562 @Override 563 public void flush() throws IOException { 564 startHandshake(); 565 synchronized (writeLock) { 566 flushInternal(); 567 } 568 } 569 570 private void flushInternal() throws IOException { 571 checkOpen(); 572 init(); 573 socketOutputStream.flush(); 574 } 575 576 private void init() throws IOException { 577 if (socketOutputStream == null) { 578 socketOutputStream = getUnderlyingOutputStream(); 579 } 580 } 581 582 private void writeToSocket() throws IOException { 583 // Write the data to the socket. 584 socketOutputStream.write(target.array(), targetArrayOffset, target.limit()); 585 } 586 } 587 588 /** 589 * Unwrap bytes read from the underlying socket. 590 */ 591 private final class SSLInputStream extends InputStream { 592 private final Object readLock = new Object(); 593 private final byte[] singleByte = new byte[1]; 594 private final ByteBuffer fromEngine; 595 private final ByteBuffer fromSocket; 596 private final int fromSocketArrayOffset; 597 private InputStream socketInputStream; 598 599 SSLInputStream() { 600 fromEngine = ByteBuffer.allocateDirect(engine.getSession().getApplicationBufferSize()); 601 // Initially fromEngine.remaining() == 0. 602 fromEngine.flip(); 603 fromSocket = ByteBuffer.allocate(engine.getSession().getPacketBufferSize()); 604 fromSocketArrayOffset = fromSocket.arrayOffset(); 605 } 606 607 @Override 608 public void close() throws IOException { 609 ConscryptEngineSocket.this.close(); 610 } 611 612 @Override 613 public int read() throws IOException { 614 startHandshake(); 615 synchronized (readLock) { 616 // Handle returning of -1 if EOF is reached. 617 int count = read(singleByte, 0, 1); 618 if (count == -1) { 619 // Handle EOF. 620 return -1; 621 } 622 if (count != 1) { 623 throw new SSLException("read incorrect number of bytes " + count); 624 } 625 return (int) singleByte[0]; 626 } 627 } 628 629 @Override 630 public int read(byte[] b) throws IOException { 631 startHandshake(); 632 synchronized (readLock) { 633 return read(b, 0, b.length); 634 } 635 } 636 637 @Override 638 public int read(byte[] b, int off, int len) throws IOException { 639 startHandshake(); 640 synchronized (readLock) { 641 return readInternal(b, off, len); 642 } 643 } 644 645 @Override 646 public int available() throws IOException { 647 startHandshake(); 648 synchronized (readLock) { 649 init(); 650 return fromEngine.remaining() 651 + (fromSocket.hasRemaining() || socketInputStream.available() > 0 ? 1 : 0); 652 } 653 } 654 655 private boolean isHandshaking(HandshakeStatus status) { 656 switch(status) { 657 case NEED_TASK: 658 case NEED_WRAP: 659 case NEED_UNWRAP: 660 return true; 661 default: 662 return false; 663 } 664 } 665 666 private int readInternal(byte[] b, int off, int len) throws IOException { 667 Platform.blockGuardOnNetwork(); 668 checkOpen(); 669 670 // Make sure the input stream has been created. 671 init(); 672 673 for (;;) { 674 // Serve any remaining data from the engine first. 675 if (fromEngine.remaining() > 0) { 676 int readFromEngine = Math.min(fromEngine.remaining(), len); 677 fromEngine.get(b, off, readFromEngine); 678 return readFromEngine; 679 } 680 681 // Try to unwrap any data already in the socket buffer. 682 boolean needMoreDataFromSocket = true; 683 684 // Unwrap the unencrypted bytes into the engine buffer. 685 fromSocket.flip(); 686 fromEngine.clear(); 687 688 boolean engineHandshaking = isHandshaking(engine.getHandshakeStatus()); 689 SSLEngineResult engineResult = engine.unwrap(fromSocket, fromEngine); 690 691 // Shift any remaining data to the beginning of the buffer so that 692 // we can accommodate the next full packet. After this is called, 693 // limit will be restored to capacity and position will point just 694 // past the end of the data. 695 fromSocket.compact(); 696 fromEngine.flip(); 697 698 switch (engineResult.getStatus()) { 699 case BUFFER_UNDERFLOW: { 700 if (engineResult.bytesProduced() == 0) { 701 // Need to read more data from the socket. 702 break; 703 } 704 // Also serve the data that was produced. 705 needMoreDataFromSocket = false; 706 break; 707 } 708 case OK: { 709 // We processed the entire packet successfully... 710 711 if (!engineHandshaking && isHandshaking(engineResult.getHandshakeStatus()) 712 && isHandshakeFinished()) { 713 // The received packet is the beginning of a renegotiation handshake. 714 // Perform another handshake. 715 renegotiate(); 716 return 0; 717 } 718 719 needMoreDataFromSocket = false; 720 break; 721 } 722 case CLOSED: { 723 // EOF 724 return -1; 725 } 726 default: { 727 // Anything else is an error. 728 throw new SSLException( 729 "Unexpected engine result " + engineResult.getStatus()); 730 } 731 } 732 733 if (!needMoreDataFromSocket && engineResult.bytesProduced() == 0) { 734 // Read successfully, but produced no data. Possibly part of a 735 // handshake. 736 return 0; 737 } 738 739 // Read more data from the socket. 740 if (needMoreDataFromSocket && readFromSocket() == -1) { 741 // Failed to read the next encrypted packet before reaching EOF. 742 return -1; 743 } 744 745 // Continue the loop and return the data from the engine buffer. 746 } 747 } 748 749 private boolean isHandshakeFinished() { 750 synchronized (stateLock) { 751 return state >= STATE_READY_HANDSHAKE_CUT_THROUGH; 752 } 753 } 754 755 /** 756 * Processes a renegotiation received from the remote peer. 757 */ 758 private void renegotiate() throws IOException { 759 synchronized (handshakeLock) { 760 doHandshake(); 761 } 762 } 763 764 private void init() throws IOException { 765 if (socketInputStream == null) { 766 socketInputStream = getUnderlyingInputStream(); 767 } 768 } 769 770 private int readFromSocket() throws IOException { 771 try { 772 // Read directly to the underlying array and increment the buffer position if 773 // appropriate. 774 int pos = fromSocket.position(); 775 int lim = fromSocket.limit(); 776 int read = socketInputStream.read( 777 fromSocket.array(), fromSocketArrayOffset + pos, lim - pos); 778 779 if (read > 0) { 780 fromSocket.position(pos + read); 781 } 782 return read; 783 } catch (EOFException e) { 784 return -1; 785 } 786 } 787 } 788 } 789