1 /* 2 * Copyright (C) 2007 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.apache.harmony.xnet.provider.jsse; 18 19 import dalvik.system.BlockGuard; 20 import dalvik.system.CloseGuard; 21 22 import java.io.FileDescriptor; 23 import java.io.IOException; 24 import java.io.InputStream; 25 import java.io.OutputStream; 26 import java.net.InetAddress; 27 import java.net.Socket; 28 import java.net.SocketException; 29 import java.security.PrivateKey; 30 import java.security.SecureRandom; 31 import java.security.cert.CertificateEncodingException; 32 import java.security.cert.CertificateException; 33 import java.security.cert.X509Certificate; 34 import java.util.ArrayList; 35 import java.util.Arrays; 36 import java.util.HashSet; 37 import java.util.Set; 38 import javax.net.ssl.HandshakeCompletedEvent; 39 import javax.net.ssl.HandshakeCompletedListener; 40 import javax.net.ssl.SSLException; 41 import javax.net.ssl.SSLHandshakeException; 42 import javax.net.ssl.SSLProtocolException; 43 import javax.net.ssl.SSLSession; 44 import javax.net.ssl.X509TrustManager; 45 import javax.security.auth.x500.X500Principal; 46 import libcore.io.Streams; 47 import org.apache.harmony.security.provider.cert.X509CertImpl; 48 49 /** 50 * Implementation of the class OpenSSLSocketImpl based on OpenSSL. 51 * <p> 52 * This class only supports SSLv3 and TLSv1. This should be documented elsewhere 53 * later, for example in the package.html or a separate reference document. 54 * <p> 55 * Extensions to SSLSocket include: 56 * <ul> 57 * <li>handshake timeout 58 * <li>compression methods 59 * <li>session tickets 60 * <li>Server Name Indication 61 * </ul> 62 */ 63 public class OpenSSLSocketImpl 64 extends javax.net.ssl.SSLSocket 65 implements NativeCrypto.SSLHandshakeCallbacks { 66 67 private int sslNativePointer; 68 private InputStream is; 69 private OutputStream os; 70 private final Object handshakeLock = new Object(); 71 private final Object readLock = new Object(); 72 private final Object writeLock = new Object(); 73 private SSLParametersImpl sslParameters; 74 private String[] enabledProtocols; 75 private String[] enabledCipherSuites; 76 private String[] enabledCompressionMethods; 77 private boolean useSessionTickets; 78 private String hostname; 79 private OpenSSLSessionImpl sslSession; 80 private final Socket socket; 81 private boolean autoClose; 82 private boolean handshakeStarted = false; 83 private final CloseGuard guard = CloseGuard.get(); 84 85 /** 86 * Not set to true until the update from native that tells us the 87 * full handshake is complete, since SSL_do_handshake can return 88 * before the handshake is completely done due to 89 * handshake_cutthrough support. 90 */ 91 private boolean handshakeCompleted = false; 92 93 private ArrayList<HandshakeCompletedListener> listeners; 94 95 /** 96 * Local cache of timeout to avoid getsockopt on every read and 97 * write for non-wrapped sockets. Note that 98 * OpenSSLSocketImplWrapper overrides setSoTimeout and 99 * getSoTimeout to delegate to the wrapped socket. 100 */ 101 private int timeoutMilliseconds = 0; 102 103 private int handshakeTimeoutMilliseconds = -1; // -1 = same as timeout; 0 = infinite 104 private String wrappedHost; 105 private int wrappedPort; 106 107 protected OpenSSLSocketImpl(SSLParametersImpl sslParameters) throws IOException { 108 this.socket = this; 109 init(sslParameters); 110 } 111 112 protected OpenSSLSocketImpl(SSLParametersImpl sslParameters, 113 String[] enabledProtocols, 114 String[] enabledCipherSuites, 115 String[] enabledCompressionMethods) throws IOException { 116 this.socket = this; 117 init(sslParameters, enabledProtocols, enabledCipherSuites, enabledCompressionMethods); 118 } 119 120 protected OpenSSLSocketImpl(String host, int port, SSLParametersImpl sslParameters) 121 throws IOException { 122 super(host, port); 123 this.socket = this; 124 init(sslParameters); 125 } 126 127 protected OpenSSLSocketImpl(InetAddress address, int port, SSLParametersImpl sslParameters) 128 throws IOException { 129 super(address, port); 130 this.socket = this; 131 init(sslParameters); 132 } 133 134 135 protected OpenSSLSocketImpl(String host, int port, 136 InetAddress clientAddress, int clientPort, 137 SSLParametersImpl sslParameters) throws IOException { 138 super(host, port, clientAddress, clientPort); 139 this.socket = this; 140 init(sslParameters); 141 } 142 143 protected OpenSSLSocketImpl(InetAddress address, int port, 144 InetAddress clientAddress, int clientPort, 145 SSLParametersImpl sslParameters) throws IOException { 146 super(address, port, clientAddress, clientPort); 147 this.socket = this; 148 init(sslParameters); 149 } 150 151 /** 152 * Create an SSL socket that wraps another socket. Invoked by 153 * OpenSSLSocketImplWrapper constructor. 154 */ 155 protected OpenSSLSocketImpl(Socket socket, String host, int port, 156 boolean autoClose, SSLParametersImpl sslParameters) throws IOException { 157 this.socket = socket; 158 this.wrappedHost = host; 159 this.wrappedPort = port; 160 this.autoClose = autoClose; 161 init(sslParameters); 162 163 // this.timeout is not set intentionally. 164 // OpenSSLSocketImplWrapper.getSoTimeout will delegate timeout 165 // to wrapped socket 166 } 167 168 /** 169 * Initialize the SSL socket and set the certificates for the 170 * future handshaking. 171 */ 172 private void init(SSLParametersImpl sslParameters) throws IOException { 173 init(sslParameters, 174 NativeCrypto.getSupportedProtocols(), 175 NativeCrypto.getDefaultCipherSuites(), 176 NativeCrypto.getDefaultCompressionMethods()); 177 } 178 179 /** 180 * Initialize the SSL socket and set the certificates for the 181 * future handshaking. 182 */ 183 private void init(SSLParametersImpl sslParameters, 184 String[] enabledProtocols, 185 String[] enabledCipherSuites, 186 String[] enabledCompressionMethods) throws IOException { 187 this.sslParameters = sslParameters; 188 this.enabledProtocols = enabledProtocols; 189 this.enabledCipherSuites = enabledCipherSuites; 190 this.enabledCompressionMethods = enabledCompressionMethods; 191 } 192 193 /** 194 * Gets the suitable session reference from the session cache container. 195 */ 196 private OpenSSLSessionImpl getCachedClientSession(ClientSessionContext sessionContext) { 197 if (super.getInetAddress() == null || 198 super.getInetAddress().getHostAddress() == null || 199 super.getInetAddress().getHostName() == null) { 200 return null; 201 } 202 OpenSSLSessionImpl session = (OpenSSLSessionImpl) sessionContext.getSession( 203 super.getInetAddress().getHostName(), 204 super.getPort()); 205 if (session == null) { 206 return null; 207 } 208 209 String protocol = session.getProtocol(); 210 boolean protocolFound = false; 211 for (String enabledProtocol : enabledProtocols) { 212 if (protocol.equals(enabledProtocol)) { 213 protocolFound = true; 214 break; 215 } 216 } 217 if (!protocolFound) { 218 return null; 219 } 220 221 String cipherSuite = session.getCipherSuite(); 222 boolean cipherSuiteFound = false; 223 for (String enabledCipherSuite : enabledCipherSuites) { 224 if (cipherSuite.equals(enabledCipherSuite)) { 225 cipherSuiteFound = true; 226 break; 227 } 228 } 229 if (!cipherSuiteFound) { 230 return null; 231 } 232 233 String compressionMethod = session.getCompressionMethod(); 234 boolean compressionMethodFound = false; 235 for (String enabledCompressionMethod : enabledCompressionMethods) { 236 if (compressionMethod.equals(enabledCompressionMethod)) { 237 compressionMethodFound = true; 238 break; 239 } 240 } 241 if (!compressionMethodFound) { 242 return null; 243 } 244 245 return session; 246 } 247 248 /** 249 * Starts a TLS/SSL handshake on this connection using some native methods 250 * from the OpenSSL library. It can negotiate new encryption keys, change 251 * cipher suites, or initiate a new session. The certificate chain is 252 * verified if the correspondent property in java.Security is set. All 253 * listeners are notified at the end of the TLS/SSL handshake. 254 */ 255 @Override 256 public void startHandshake() throws IOException { 257 startHandshake(true); 258 } 259 260 private void checkOpen() throws SocketException { 261 if (isClosed()) { 262 throw new SocketException("Socket is closed"); 263 } 264 } 265 266 /** 267 * Perform the handshake 268 * 269 * @param full If true, disable handshake cutthrough for a fully synchronous handshake 270 */ 271 public synchronized void startHandshake(boolean full) throws IOException { 272 synchronized (handshakeLock) { 273 checkOpen(); 274 if (!handshakeStarted) { 275 handshakeStarted = true; 276 } else { 277 return; 278 } 279 } 280 281 // note that this modifies the global seed, not something specific to the connection 282 final int seedLengthInBytes = NativeCrypto.RAND_SEED_LENGTH_IN_BYTES; 283 final SecureRandom secureRandom = sslParameters.getSecureRandomMember(); 284 if (secureRandom == null) { 285 NativeCrypto.RAND_load_file("/dev/urandom", seedLengthInBytes); 286 } else { 287 NativeCrypto.RAND_seed(secureRandom.generateSeed(seedLengthInBytes)); 288 } 289 290 final boolean client = sslParameters.getUseClientMode(); 291 292 final int sslCtxNativePointer = (client) ? 293 sslParameters.getClientSessionContext().sslCtxNativePointer : 294 sslParameters.getServerSessionContext().sslCtxNativePointer; 295 296 this.sslNativePointer = 0; 297 boolean exception = true; 298 try { 299 sslNativePointer = NativeCrypto.SSL_new(sslCtxNativePointer); 300 guard.open("close"); 301 302 // setup server certificates and private keys. 303 // clients will receive a call back to request certificates. 304 if (!client) { 305 Set<String> keyTypes = new HashSet<String>(); 306 for (String enabledCipherSuite : enabledCipherSuites) { 307 if (enabledCipherSuite.equals(NativeCrypto.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) { 308 continue; 309 } 310 String keyType = CipherSuite.getByName(enabledCipherSuite).getServerKeyType(); 311 if (keyType != null) { 312 keyTypes.add(keyType); 313 } 314 } 315 for (String keyType : keyTypes) { 316 try { 317 setCertificate(sslParameters.getKeyManager().chooseServerAlias(keyType, 318 null, 319 this)); 320 } catch (CertificateEncodingException e) { 321 throw new IOException(e); 322 } 323 } 324 } 325 326 NativeCrypto.setEnabledProtocols(sslNativePointer, enabledProtocols); 327 NativeCrypto.setEnabledCipherSuites(sslNativePointer, enabledCipherSuites); 328 if (enabledCompressionMethods.length != 0) { 329 NativeCrypto.setEnabledCompressionMethods(sslNativePointer, 330 enabledCompressionMethods); 331 } 332 if (useSessionTickets) { 333 NativeCrypto.SSL_clear_options(sslNativePointer, NativeCrypto.SSL_OP_NO_TICKET); 334 } 335 if (hostname != null) { 336 NativeCrypto.SSL_set_tlsext_host_name(sslNativePointer, hostname); 337 } 338 339 boolean enableSessionCreation = sslParameters.getEnableSessionCreation(); 340 if (!enableSessionCreation) { 341 NativeCrypto.SSL_set_session_creation_enabled(sslNativePointer, 342 enableSessionCreation); 343 } 344 345 AbstractSessionContext sessionContext; 346 if (client) { 347 // look for client session to reuse 348 ClientSessionContext clientSessionContext = sslParameters.getClientSessionContext(); 349 sessionContext = clientSessionContext; 350 OpenSSLSessionImpl session = getCachedClientSession(clientSessionContext); 351 if (session != null) { 352 NativeCrypto.SSL_set_session(sslNativePointer, 353 session.sslSessionNativePointer); 354 } 355 } else { 356 sessionContext = sslParameters.getServerSessionContext(); 357 } 358 359 // setup peer certificate verification 360 if (client) { 361 // TODO support for anonymous cipher would require us to 362 // conditionally use SSL_VERIFY_NONE 363 } else { 364 // needing client auth takes priority... 365 boolean certRequested; 366 if (sslParameters.getNeedClientAuth()) { 367 NativeCrypto.SSL_set_verify(sslNativePointer, 368 NativeCrypto.SSL_VERIFY_PEER 369 | NativeCrypto.SSL_VERIFY_FAIL_IF_NO_PEER_CERT); 370 certRequested = true; 371 // ... over just wanting it... 372 } else if (sslParameters.getWantClientAuth()) { 373 NativeCrypto.SSL_set_verify(sslNativePointer, 374 NativeCrypto.SSL_VERIFY_PEER); 375 certRequested = true; 376 // ... and it defaults properly so don't call SSL_set_verify in the common case. 377 } else { 378 certRequested = false; 379 } 380 381 if (certRequested) { 382 X509TrustManager trustManager = sslParameters.getTrustManager(); 383 X509Certificate[] issuers = trustManager.getAcceptedIssuers(); 384 if (issuers != null && issuers.length != 0) { 385 byte[][] issuersBytes; 386 try { 387 issuersBytes = NativeCrypto.encodeIssuerX509Principals(issuers); 388 } catch (CertificateEncodingException e) { 389 throw new IOException("Problem encoding principals", e); 390 } 391 NativeCrypto.SSL_set_client_CA_list(sslNativePointer, issuersBytes); 392 } 393 } 394 } 395 396 if (client && full) { 397 // we want to do a full synchronous handshake, so turn off cutthrough 398 NativeCrypto.SSL_clear_mode(sslNativePointer, 399 NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH); 400 } 401 402 // Temporarily use a different timeout for the handshake process 403 int savedTimeoutMilliseconds = getSoTimeout(); 404 if (handshakeTimeoutMilliseconds >= 0) { 405 setSoTimeout(handshakeTimeoutMilliseconds); 406 } 407 408 int sslSessionNativePointer; 409 try { 410 sslSessionNativePointer = NativeCrypto.SSL_do_handshake(sslNativePointer, 411 socket.getFileDescriptor$(), this, getSoTimeout(), client); 412 } catch (CertificateException e) { 413 SSLHandshakeException wrapper = new SSLHandshakeException(e.getMessage()); 414 wrapper.initCause(e); 415 throw wrapper; 416 } 417 byte[] sessionId = NativeCrypto.SSL_SESSION_session_id(sslSessionNativePointer); 418 sslSession = (OpenSSLSessionImpl) sessionContext.getSession(sessionId); 419 if (sslSession != null) { 420 sslSession.lastAccessedTime = System.currentTimeMillis(); 421 NativeCrypto.SSL_SESSION_free(sslSessionNativePointer); 422 } else { 423 if (!enableSessionCreation) { 424 // Should have been prevented by NativeCrypto.SSL_set_session_creation_enabled 425 throw new IllegalStateException("SSL Session may not be created"); 426 } 427 X509Certificate[] localCertificates 428 = createCertChain(NativeCrypto.SSL_get_certificate(sslNativePointer)); 429 X509Certificate[] peerCertificates 430 = createCertChain(NativeCrypto.SSL_get_peer_cert_chain(sslNativePointer)); 431 if (wrappedHost == null) { 432 sslSession = new OpenSSLSessionImpl(sslSessionNativePointer, 433 localCertificates, peerCertificates, 434 super.getInetAddress().getHostName(), 435 super.getPort(), sessionContext); 436 } else { 437 sslSession = new OpenSSLSessionImpl(sslSessionNativePointer, 438 localCertificates, peerCertificates, 439 wrappedHost, wrappedPort, 440 sessionContext); 441 } 442 // if not, putSession later in handshakeCompleted() callback 443 if (handshakeCompleted) { 444 sessionContext.putSession(sslSession); 445 } 446 } 447 448 // Restore the original timeout now that the handshake is complete 449 if (handshakeTimeoutMilliseconds >= 0) { 450 setSoTimeout(savedTimeoutMilliseconds); 451 } 452 453 // if not, notifyHandshakeCompletedListeners later in handshakeCompleted() callback 454 if (handshakeCompleted) { 455 notifyHandshakeCompletedListeners(); 456 } 457 458 exception = false; 459 } catch (SSLProtocolException e) { 460 throw new SSLHandshakeException(e); 461 } finally { 462 // on exceptional exit, treat the socket as closed 463 if (exception) { 464 close(); 465 } 466 } 467 } 468 469 /** 470 * Return a possibly null array of X509Certificates given the 471 * possibly null array of DER encoded bytes. 472 */ 473 private static X509Certificate[] createCertChain(byte[][] certificatesBytes) { 474 if (certificatesBytes == null) { 475 return null; 476 } 477 X509Certificate[] certificates = new X509Certificate[certificatesBytes.length]; 478 for (int i = 0; i < certificatesBytes.length; i++) { 479 try { 480 certificates[i] = new X509CertImpl(certificatesBytes[i]); 481 } catch (IOException e) { 482 return null; 483 } 484 } 485 return certificates; 486 } 487 488 private void setCertificate(String alias) throws CertificateEncodingException, SSLException { 489 if (alias == null) { 490 return; 491 } 492 PrivateKey privateKey = sslParameters.getKeyManager().getPrivateKey(alias); 493 if (privateKey == null) { 494 return; 495 } 496 X509Certificate[] certificates = sslParameters.getKeyManager().getCertificateChain(alias); 497 if (certificates == null) { 498 return; 499 } 500 501 byte[] privateKeyBytes = privateKey.getEncoded(); 502 byte[][] certificateBytes = NativeCrypto.encodeCertificates(certificates); 503 NativeCrypto.SSL_use_PrivateKey(sslNativePointer, privateKeyBytes); 504 NativeCrypto.SSL_use_certificate(sslNativePointer, certificateBytes); 505 506 // checks the last installed private key and certificate, 507 // so need to do this once per loop iteration 508 NativeCrypto.SSL_check_private_key(sslNativePointer); 509 } 510 511 @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks / client_cert_cb 512 public void clientCertificateRequested(byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals) 513 throws CertificateEncodingException, SSLException { 514 515 String[] keyTypes = new String[keyTypeBytes.length]; 516 for (int i = 0; i < keyTypeBytes.length; i++) { 517 keyTypes[i] = CipherSuite.getClientKeyType(keyTypeBytes[i]); 518 } 519 520 X500Principal[] issuers; 521 if (asn1DerEncodedPrincipals == null) { 522 issuers = null; 523 } else { 524 issuers = new X500Principal[asn1DerEncodedPrincipals.length]; 525 for (int i = 0; i < asn1DerEncodedPrincipals.length; i++) { 526 issuers[i] = new X500Principal(asn1DerEncodedPrincipals[i]); 527 } 528 } 529 setCertificate(sslParameters.getKeyManager().chooseClientAlias(keyTypes, issuers, this)); 530 } 531 532 @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks / info_callback 533 public void handshakeCompleted() { 534 handshakeCompleted = true; 535 536 // If sslSession is null, the handshake was completed during 537 // the call to NativeCrypto.SSL_do_handshake and not during a 538 // later read operation. That means we do not need to fix up 539 // the SSLSession and session cache or notify 540 // HandshakeCompletedListeners, it will be done in 541 // startHandshake. 542 if (sslSession == null) { 543 return; 544 } 545 546 // reset session id from the native pointer and update the 547 // appropriate cache. 548 sslSession.resetId(); 549 AbstractSessionContext sessionContext = 550 (sslParameters.getUseClientMode()) 551 ? sslParameters.getClientSessionContext() 552 : sslParameters.getServerSessionContext(); 553 sessionContext.putSession(sslSession); 554 555 // let listeners know we are finally done 556 notifyHandshakeCompletedListeners(); 557 } 558 559 private void notifyHandshakeCompletedListeners() { 560 if (listeners != null && !listeners.isEmpty()) { 561 // notify the listeners 562 HandshakeCompletedEvent event = 563 new HandshakeCompletedEvent(this, sslSession); 564 for (HandshakeCompletedListener listener : listeners) { 565 try { 566 listener.handshakeCompleted(event); 567 } catch (RuntimeException e) { 568 // The RI runs the handlers in a separate thread, 569 // which we do not. But we try to preserve their 570 // behavior of logging a problem and not killing 571 // the handshaking thread just because a listener 572 // has a problem. 573 Thread thread = Thread.currentThread(); 574 thread.getUncaughtExceptionHandler().uncaughtException(thread, e); 575 } 576 } 577 } 578 } 579 580 @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks 581 @Override public void verifyCertificateChain(byte[][] bytes, String authMethod) 582 throws CertificateException { 583 try { 584 if (bytes == null || bytes.length == 0) { 585 throw new SSLException("Peer sent no certificate"); 586 } 587 X509Certificate[] peerCertificateChain = new X509Certificate[bytes.length]; 588 for (int i = 0; i < bytes.length; i++) { 589 peerCertificateChain[i] = new X509CertImpl(bytes[i]); 590 } 591 boolean client = sslParameters.getUseClientMode(); 592 if (client) { 593 sslParameters.getTrustManager().checkServerTrusted(peerCertificateChain, 594 authMethod); 595 } else { 596 String authType = peerCertificateChain[0].getPublicKey().getAlgorithm(); 597 sslParameters.getTrustManager().checkClientTrusted(peerCertificateChain, 598 authType); 599 } 600 601 } catch (CertificateException e) { 602 throw e; 603 } catch (RuntimeException e) { 604 throw e; 605 } catch (Exception e) { 606 throw new RuntimeException(e); 607 } 608 } 609 610 @Override public InputStream getInputStream() throws IOException { 611 checkOpen(); 612 synchronized (this) { 613 if (is == null) { 614 is = new SSLInputStream(); 615 } 616 617 return is; 618 } 619 } 620 621 @Override public OutputStream getOutputStream() throws IOException { 622 checkOpen(); 623 synchronized (this) { 624 if (os == null) { 625 os = new SSLOutputStream(); 626 } 627 628 return os; 629 } 630 } 631 632 /** 633 * This inner class provides input data stream functionality 634 * for the OpenSSL native implementation. It is used to 635 * read data received via SSL protocol. 636 */ 637 private class SSLInputStream extends InputStream { 638 SSLInputStream() throws IOException { 639 /** 640 /* Note: When startHandshake() throws an exception, no 641 * SSLInputStream object will be created. 642 */ 643 OpenSSLSocketImpl.this.startHandshake(false); 644 } 645 646 /** 647 * Reads one byte. If there is no data in the underlying buffer, 648 * this operation can block until the data will be 649 * available. 650 * @return read value. 651 * @throws <code>IOException</code> 652 */ 653 @Override 654 public int read() throws IOException { 655 return Streams.readSingleByte(this); 656 } 657 658 /** 659 * Method acts as described in spec for superclass. 660 * @see java.io.InputStream#read(byte[],int,int) 661 */ 662 @Override 663 public int read(byte[] buf, int offset, int byteCount) throws IOException { 664 BlockGuard.getThreadPolicy().onNetwork(); 665 synchronized (readLock) { 666 checkOpen(); 667 Arrays.checkOffsetAndCount(buf.length, offset, byteCount); 668 if (byteCount == 0) { 669 return 0; 670 } 671 return NativeCrypto.SSL_read(sslNativePointer, socket.getFileDescriptor$(), 672 OpenSSLSocketImpl.this, buf, offset, byteCount, getSoTimeout()); 673 } 674 } 675 } 676 677 /** 678 * This inner class provides output data stream functionality 679 * for the OpenSSL native implementation. It is used to 680 * write data according to the encryption parameters given in SSL context. 681 */ 682 private class SSLOutputStream extends OutputStream { 683 SSLOutputStream() throws IOException { 684 /** 685 /* Note: When startHandshake() throws an exception, no 686 * SSLOutputStream object will be created. 687 */ 688 OpenSSLSocketImpl.this.startHandshake(false); 689 } 690 691 /** 692 * Method acts as described in spec for superclass. 693 * @see java.io.OutputStream#write(int) 694 */ 695 @Override 696 public void write(int oneByte) throws IOException { 697 Streams.writeSingleByte(this, oneByte); 698 } 699 700 /** 701 * Method acts as described in spec for superclass. 702 * @see java.io.OutputStream#write(byte[],int,int) 703 */ 704 @Override 705 public void write(byte[] buf, int offset, int byteCount) throws IOException { 706 BlockGuard.getThreadPolicy().onNetwork(); 707 synchronized (writeLock) { 708 checkOpen(); 709 Arrays.checkOffsetAndCount(buf.length, offset, byteCount); 710 if (byteCount == 0) { 711 return; 712 } 713 NativeCrypto.SSL_write(sslNativePointer, socket.getFileDescriptor$(), 714 OpenSSLSocketImpl.this, buf, offset, byteCount); 715 } 716 } 717 } 718 719 720 @Override public SSLSession getSession() { 721 if (sslSession == null) { 722 try { 723 startHandshake(true); 724 } catch (IOException e) { 725 // return an invalid session with 726 // invalid cipher suite of "SSL_NULL_WITH_NULL_NULL" 727 return SSLSessionImpl.NULL_SESSION; 728 } 729 } 730 return sslSession; 731 } 732 733 @Override public void addHandshakeCompletedListener( 734 HandshakeCompletedListener listener) { 735 if (listener == null) { 736 throw new IllegalArgumentException("Provided listener is null"); 737 } 738 if (listeners == null) { 739 listeners = new ArrayList<HandshakeCompletedListener>(); 740 } 741 listeners.add(listener); 742 } 743 744 @Override public void removeHandshakeCompletedListener( 745 HandshakeCompletedListener listener) { 746 if (listener == null) { 747 throw new IllegalArgumentException("Provided listener is null"); 748 } 749 if (listeners == null) { 750 throw new IllegalArgumentException( 751 "Provided listener is not registered"); 752 } 753 if (!listeners.remove(listener)) { 754 throw new IllegalArgumentException( 755 "Provided listener is not registered"); 756 } 757 } 758 759 @Override public boolean getEnableSessionCreation() { 760 return sslParameters.getEnableSessionCreation(); 761 } 762 763 @Override public void setEnableSessionCreation(boolean flag) { 764 sslParameters.setEnableSessionCreation(flag); 765 } 766 767 @Override public String[] getSupportedCipherSuites() { 768 return NativeCrypto.getSupportedCipherSuites(); 769 } 770 771 @Override public String[] getEnabledCipherSuites() { 772 return enabledCipherSuites.clone(); 773 } 774 775 @Override public void setEnabledCipherSuites(String[] suites) { 776 enabledCipherSuites = NativeCrypto.checkEnabledCipherSuites(suites); 777 } 778 779 @Override public String[] getSupportedProtocols() { 780 return NativeCrypto.getSupportedProtocols(); 781 } 782 783 @Override public String[] getEnabledProtocols() { 784 return enabledProtocols.clone(); 785 } 786 787 @Override public void setEnabledProtocols(String[] protocols) { 788 enabledProtocols = NativeCrypto.checkEnabledProtocols(protocols); 789 } 790 791 /** 792 * The names of the compression methods that may be used on this SSL 793 * connection. 794 * @return an array of compression methods 795 */ 796 public String[] getSupportedCompressionMethods() { 797 return NativeCrypto.getSupportedCompressionMethods(); 798 } 799 800 /** 801 * The names of the compression methods versions that are in use 802 * on this SSL connection. 803 * 804 * @return an array of compression methods 805 */ 806 public String[] getEnabledCompressionMethods() { 807 return enabledCompressionMethods.clone(); 808 } 809 810 /** 811 * Enables compression methods listed by getSupportedCompressionMethods(). 812 * 813 * @throws IllegalArgumentException when one or more of the names in the 814 * array are not supported, or when the array is null. 815 */ 816 public void setEnabledCompressionMethods(String[] methods) { 817 enabledCompressionMethods = NativeCrypto.checkEnabledCompressionMethods(methods); 818 } 819 820 /** 821 * This method enables session ticket support. 822 * 823 * @param useSessionTickets True to enable session tickets 824 */ 825 public void setUseSessionTickets(boolean useSessionTickets) { 826 this.useSessionTickets = useSessionTickets; 827 } 828 829 /** 830 * This method enables Server Name Indication 831 * 832 * @param hostname the desired SNI hostname, or null to disable 833 */ 834 public void setHostname(String hostname) { 835 this.hostname = hostname; 836 } 837 838 @Override public boolean getUseClientMode() { 839 return sslParameters.getUseClientMode(); 840 } 841 842 @Override public void setUseClientMode(boolean mode) { 843 if (handshakeStarted) { 844 throw new IllegalArgumentException( 845 "Could not change the mode after the initial handshake has begun."); 846 } 847 sslParameters.setUseClientMode(mode); 848 } 849 850 @Override public boolean getWantClientAuth() { 851 return sslParameters.getWantClientAuth(); 852 } 853 854 @Override public boolean getNeedClientAuth() { 855 return sslParameters.getNeedClientAuth(); 856 } 857 858 @Override public void setNeedClientAuth(boolean need) { 859 sslParameters.setNeedClientAuth(need); 860 } 861 862 @Override public void setWantClientAuth(boolean want) { 863 sslParameters.setWantClientAuth(want); 864 } 865 866 @Override public void sendUrgentData(int data) throws IOException { 867 throw new SocketException("Method sendUrgentData() is not supported."); 868 } 869 870 @Override public void setOOBInline(boolean on) throws SocketException { 871 throw new SocketException("Methods sendUrgentData, setOOBInline are not supported."); 872 } 873 874 @Override public void setSoTimeout(int timeoutMilliseconds) throws SocketException { 875 super.setSoTimeout(timeoutMilliseconds); 876 this.timeoutMilliseconds = timeoutMilliseconds; 877 } 878 879 @Override public int getSoTimeout() throws SocketException { 880 return timeoutMilliseconds; 881 } 882 883 /** 884 * Set the handshake timeout on this socket. This timeout is specified in 885 * milliseconds and will be used only during the handshake process. 886 */ 887 public void setHandshakeTimeout(int timeoutMilliseconds) throws SocketException { 888 this.handshakeTimeoutMilliseconds = timeoutMilliseconds; 889 } 890 891 @Override public void close() throws IOException { 892 // TODO: Close SSL sockets using a background thread so they close gracefully. 893 894 synchronized (handshakeLock) { 895 if (!handshakeStarted) { 896 // prevent further attempts to start handshake 897 handshakeStarted = true; 898 899 synchronized (this) { 900 free(); 901 902 if (socket != this) { 903 if (autoClose && !socket.isClosed()) socket.close(); 904 } else { 905 if (!super.isClosed()) super.close(); 906 } 907 } 908 909 return; 910 } 911 } 912 913 NativeCrypto.SSL_interrupt(sslNativePointer); 914 915 synchronized (this) { 916 synchronized (writeLock) { 917 synchronized (readLock) { 918 919 // Shut down the SSL connection, per se. 920 try { 921 if (handshakeStarted) { 922 BlockGuard.getThreadPolicy().onNetwork(); 923 NativeCrypto.SSL_shutdown(sslNativePointer, socket.getFileDescriptor$(), 924 this); 925 } 926 } catch (IOException ignored) { 927 /* 928 * Note that although close() can throw 929 * IOException, the RI does not throw if there 930 * is problem sending a "close notify" which 931 * can happen if the underlying socket is closed. 932 */ 933 } finally { 934 /* 935 * Even if the above call failed, it is still safe to free 936 * the native structs, and we need to do so lest we leak 937 * memory. 938 */ 939 free(); 940 941 if (socket != this) { 942 if (autoClose && !socket.isClosed()) { 943 socket.close(); 944 } 945 } else { 946 if (!super.isClosed()) { 947 super.close(); 948 } 949 } 950 } 951 } 952 } 953 } 954 } 955 956 private void free() { 957 if (sslNativePointer == 0) { 958 return; 959 } 960 NativeCrypto.SSL_free(sslNativePointer); 961 sslNativePointer = 0; 962 guard.close(); 963 } 964 965 @Override protected void finalize() throws Throwable { 966 try { 967 /* 968 * Just worry about our own state. Notably we do not try and 969 * close anything. The SocketImpl, either our own 970 * PlainSocketImpl, or the Socket we are wrapping, will do 971 * that. This might mean we do not properly SSL_shutdown, but 972 * if you want to do that, properly close the socket yourself. 973 * 974 * The reason why we don't try to SSL_shutdown, is that there 975 * can be a race between finalizers where the PlainSocketImpl 976 * finalizer runs first and closes the socket. However, in the 977 * meanwhile, the underlying file descriptor could be reused 978 * for another purpose. If we call SSL_shutdown, the 979 * underlying socket BIOs still have the old file descriptor 980 * and will write the close notify to some unsuspecting 981 * reader. 982 */ 983 if (guard != null) { 984 guard.warnIfOpen(); 985 } 986 free(); 987 } finally { 988 super.finalize(); 989 } 990 } 991 992 @Override 993 public FileDescriptor getFileDescriptor$() { 994 if (socket == this) { 995 return super.getFileDescriptor$(); 996 } else { 997 return socket.getFileDescriptor$(); 998 } 999 } 1000 } 1001