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