Home | History | Annotate | Download | only in jsse
      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 java.io.FileDescriptor;
     21 import java.io.IOException;
     22 import java.io.InputStream;
     23 import java.io.OutputStream;
     24 import java.net.InetAddress;
     25 import java.net.Socket;
     26 import java.net.SocketException;
     27 import java.security.PrivateKey;
     28 import java.security.SecureRandom;
     29 import java.security.cert.CertificateEncodingException;
     30 import java.security.cert.CertificateException;
     31 import java.security.cert.X509Certificate;
     32 import java.util.ArrayList;
     33 import java.util.concurrent.atomic.AtomicInteger;
     34 import java.util.logging.Logger;
     35 import javax.net.ssl.HandshakeCompletedEvent;
     36 import javax.net.ssl.HandshakeCompletedListener;
     37 import javax.net.ssl.SSLException;
     38 import javax.net.ssl.SSLHandshakeException;
     39 import javax.net.ssl.SSLSession;
     40 import javax.security.auth.x500.X500Principal;
     41 import org.apache.harmony.security.provider.cert.X509CertImpl;
     42 
     43 /**
     44  * Implementation of the class OpenSSLSocketImpl based on OpenSSL.
     45  * <p>
     46  * This class only supports SSLv3 and TLSv1. This should be documented elsewhere
     47  * later, for example in the package.html or a separate reference document.
     48  * <p>
     49  * Extensions to SSLSocket include:
     50  * <ul>
     51  * <li>handshake timeout
     52  * <li>compression methods
     53  * <li>session tickets
     54  * <li>Server Name Indication
     55  * </ul>
     56  */
     57 public class OpenSSLSocketImpl
     58         extends javax.net.ssl.SSLSocket
     59         implements NativeCrypto.SSLHandshakeCallbacks {
     60 
     61     private int sslNativePointer;
     62     private InputStream is;
     63     private OutputStream os;
     64     private final Object handshakeLock = new Object();
     65     private final Object readLock = new Object();
     66     private final Object writeLock = new Object();
     67     private SSLParametersImpl sslParameters;
     68     private String[] enabledProtocols;
     69     private String[] enabledCipherSuites;
     70     private String[] enabledCompressionMethods;
     71     private boolean useSessionTickets;
     72     private String hostname;
     73     private OpenSSLSessionImpl sslSession;
     74     private final Socket socket;
     75     private final FileDescriptor fd;
     76     private boolean autoClose;
     77     private boolean handshakeStarted = false;
     78 
     79     /**
     80      * Not set to true until the update from native that tells us the
     81      * full handshake is complete, since SSL_do_handshake can return
     82      * before the handshake is completely done due to
     83      * handshake_cutthrough support.
     84      */
     85     private boolean handshakeCompleted = false;
     86 
     87     private ArrayList<HandshakeCompletedListener> listeners;
     88 
     89     /**
     90      * Local cache of timeout to avoid getsockopt on every read and
     91      * write for non-wrapped sockets. Note that
     92      * OpenSSLSocketImplWrapper overrides setSoTimeout and
     93      * getSoTimeout to delegate to the wrapped socket.
     94      */
     95     private int timeoutMilliseconds = 0;
     96 
     97     // BEGIN android-added
     98     private int handshakeTimeoutMilliseconds = -1;  // -1 = same as timeout; 0 = infinite
     99     // END android-added
    100     private String wrappedHost;
    101     private int wrappedPort;
    102 
    103     private static final AtomicInteger instanceCount = new AtomicInteger(0);
    104 
    105     public static int getInstanceCount() {
    106         return instanceCount.get();
    107     }
    108 
    109     private static void updateInstanceCount(int amount) {
    110         instanceCount.addAndGet(amount);
    111     }
    112 
    113     /**
    114      * Class constructor with 1 parameter
    115      *
    116      * @param sslParameters Parameters for the SSL
    117      *            context
    118      * @throws IOException if network fails
    119      */
    120     protected OpenSSLSocketImpl(SSLParametersImpl sslParameters) throws IOException {
    121         super();
    122         this.socket = this;
    123         this.fd = NativeCrypto.getFileDescriptor(socket);
    124         init(sslParameters);
    125     }
    126 
    127     /**
    128      * Create an OpenSSLSocketImpl from an OpenSSLServerSocketImpl
    129      *
    130      * @param sslParameters Parameters for the SSL
    131      *            context
    132      * @throws IOException if network fails
    133      */
    134     protected OpenSSLSocketImpl(SSLParametersImpl sslParameters,
    135                                 String[] enabledProtocols,
    136                                 String[] enabledCipherSuites,
    137                                 String[] enabledCompressionMethods) throws IOException {
    138         super();
    139         this.socket = this;
    140         this.fd = NativeCrypto.getFileDescriptor(socket);
    141         init(sslParameters, enabledProtocols, enabledCipherSuites, enabledCompressionMethods);
    142     }
    143 
    144     /**
    145      * Class constructor with 3 parameters
    146      *
    147      * @throws IOException if network fails
    148      * @throws java.net.UnknownHostException host not defined
    149      */
    150     protected OpenSSLSocketImpl(String host, int port, SSLParametersImpl sslParameters)
    151             throws IOException {
    152         super(host, port);
    153         this.socket = this;
    154         this.fd = NativeCrypto.getFileDescriptor(socket);
    155         init(sslParameters);
    156     }
    157 
    158     /**
    159      * Class constructor with 3 parameters: 1st is InetAddress
    160      *
    161      * @throws IOException if network fails
    162      * @throws java.net.UnknownHostException host not defined
    163      */
    164     protected OpenSSLSocketImpl(InetAddress address, int port, SSLParametersImpl sslParameters)
    165             throws IOException {
    166         super(address, port);
    167         this.socket = this;
    168         this.fd = NativeCrypto.getFileDescriptor(socket);
    169         init(sslParameters);
    170     }
    171 
    172 
    173     /**
    174      * Class constructor with 5 parameters: 1st is host
    175      *
    176      * @throws IOException if network fails
    177      * @throws java.net.UnknownHostException host not defined
    178      */
    179     protected OpenSSLSocketImpl(String host, int port,
    180                                 InetAddress clientAddress, int clientPort,
    181                                 SSLParametersImpl sslParameters)
    182             throws IOException {
    183         super(host, port, clientAddress, clientPort);
    184         this.socket = this;
    185         this.fd = NativeCrypto.getFileDescriptor(socket);
    186         init(sslParameters);
    187     }
    188 
    189     /**
    190      * Class constructor with 5 parameters: 1st is InetAddress
    191      *
    192      * @throws IOException if network fails
    193      * @throws java.net.UnknownHostException host not defined
    194      */
    195     protected OpenSSLSocketImpl(InetAddress address, int port,
    196                                 InetAddress clientAddress, int clientPort,
    197                                 SSLParametersImpl sslParameters)
    198             throws IOException {
    199         super(address, port, clientAddress, clientPort);
    200         this.socket = this;
    201         this.fd = NativeCrypto.getFileDescriptor(socket);
    202         init(sslParameters);
    203     }
    204 
    205     /**
    206      * Constructor with 5 parameters: 1st is socket. Enhances an existing socket
    207      * with SSL functionality. Invoked via OpenSSLSocketImplWrapper constructor.
    208      *
    209      * @throws IOException if network fails
    210      */
    211     protected OpenSSLSocketImpl(Socket socket, String host, int port,
    212             boolean autoClose, SSLParametersImpl sslParameters) throws IOException {
    213         super();
    214         this.socket = socket;
    215         this.fd = NativeCrypto.getFileDescriptor(socket);
    216         this.wrappedHost = host;
    217         this.wrappedPort = port;
    218         this.autoClose = autoClose;
    219         init(sslParameters);
    220 
    221         // this.timeout is not set intentionally.
    222         // OpenSSLSocketImplWrapper.getSoTimeout will delegate timeout
    223         // to wrapped socket
    224     }
    225 
    226     /**
    227      * Initialize the SSL socket and set the certificates for the
    228      * future handshaking.
    229      */
    230     private void init(SSLParametersImpl sslParameters) throws IOException {
    231         init(sslParameters,
    232              NativeCrypto.getSupportedProtocols(),
    233              NativeCrypto.getDefaultCipherSuites(),
    234              NativeCrypto.getDefaultCompressionMethods());
    235     }
    236 
    237     /**
    238      * Initialize the SSL socket and set the certificates for the
    239      * future handshaking.
    240      */
    241     private void init(SSLParametersImpl sslParameters,
    242                       String[] enabledProtocols,
    243                       String[] enabledCipherSuites,
    244                       String[] enabledCompressionMethods) throws IOException {
    245         this.sslParameters = sslParameters;
    246         this.enabledProtocols = enabledProtocols;
    247         this.enabledCipherSuites = enabledCipherSuites;
    248         this.enabledCompressionMethods = enabledCompressionMethods;
    249         updateInstanceCount(1);
    250     }
    251 
    252     /**
    253      * Gets the suitable session reference from the session cache container.
    254      *
    255      * @return OpenSSLSessionImpl
    256      */
    257     private OpenSSLSessionImpl getCachedClientSession(ClientSessionContext sessionContext) {
    258         if (super.getInetAddress() == null ||
    259                 super.getInetAddress().getHostAddress() == null ||
    260                 super.getInetAddress().getHostName() == null) {
    261             return null;
    262         }
    263         OpenSSLSessionImpl session = (OpenSSLSessionImpl) sessionContext.getSession(
    264                 super.getInetAddress().getHostName(),
    265                 super.getPort());
    266         if (session == null) {
    267             return null;
    268         }
    269 
    270         String protocol = session.getProtocol();
    271         boolean protocolFound = false;
    272         for (String enabledProtocol : enabledProtocols) {
    273             if (protocol.equals(enabledProtocol)) {
    274                 protocolFound = true;
    275                 break;
    276             }
    277         }
    278         if (!protocolFound) {
    279             return null;
    280         }
    281 
    282         String cipherSuite = session.getCipherSuite();
    283         boolean cipherSuiteFound = false;
    284         for (String enabledCipherSuite : enabledCipherSuites) {
    285             if (cipherSuite.equals(enabledCipherSuite)) {
    286                 cipherSuiteFound = true;
    287                 break;
    288             }
    289         }
    290         if (!cipherSuiteFound) {
    291             return null;
    292         }
    293 
    294         String compressionMethod = session.getCompressionMethod();
    295         boolean compressionMethodFound = false;
    296         for (String enabledCompressionMethod : enabledCompressionMethods) {
    297             if (compressionMethod.equals(enabledCompressionMethod)) {
    298                 compressionMethodFound = true;
    299                 break;
    300             }
    301         }
    302         if (!compressionMethodFound) {
    303             return null;
    304         }
    305 
    306         return session;
    307     }
    308 
    309     /**
    310      * Ensures that logger is lazily loaded. The outer class seems to load
    311      * before logging is ready.
    312      */
    313     static class LoggerHolder {
    314         static final Logger logger = Logger.getLogger(OpenSSLSocketImpl.class.getName());
    315     }
    316 
    317     /**
    318      * Starts a TLS/SSL handshake on this connection using some native methods
    319      * from the OpenSSL library. It can negotiate new encryption keys, change
    320      * cipher suites, or initiate a new session. The certificate chain is
    321      * verified if the correspondent property in java.Security is set. All
    322      * listeners are notified at the end of the TLS/SSL handshake.
    323      *
    324      * @throws <code>IOException</code> if network fails
    325      */
    326     @Override
    327     public void startHandshake() throws IOException {
    328         startHandshake(true);
    329     }
    330 
    331     /**
    332      * Checks whether the socket is closed, and throws an exception.
    333      *
    334      * @throws SocketException
    335      *             if the socket is closed.
    336      */
    337     private void checkOpen() throws SocketException {
    338         if (isClosed()) {
    339             throw new SocketException("Socket is closed");
    340         }
    341     }
    342 
    343     /**
    344      * Perform the handshake
    345      * @param full If true, disable handshake cutthrough for a fully synchronous handshake
    346      */
    347     public synchronized void startHandshake(boolean full) throws IOException {
    348         synchronized (handshakeLock) {
    349             checkOpen();
    350             if (!handshakeStarted) {
    351                 handshakeStarted = true;
    352             } else {
    353                 return;
    354             }
    355         }
    356 
    357         // note that this modifies the global seed, not something specific to the connection
    358         final int seedLengthInBytes = NativeCrypto.RAND_SEED_LENGTH_IN_BYTES;
    359         final SecureRandom secureRandom = sslParameters.getSecureRandomMember();
    360         if (secureRandom == null) {
    361             NativeCrypto.RAND_load_file("/dev/urandom", seedLengthInBytes);
    362         } else {
    363             NativeCrypto.RAND_seed(secureRandom.generateSeed(seedLengthInBytes));
    364         }
    365 
    366         final boolean client = sslParameters.getUseClientMode();
    367 
    368         final int sslCtxNativePointer = (client) ?
    369             sslParameters.getClientSessionContext().sslCtxNativePointer :
    370             sslParameters.getServerSessionContext().sslCtxNativePointer;
    371 
    372         this.sslNativePointer = NativeCrypto.SSL_new(sslCtxNativePointer);
    373 
    374         // setup server certificates and private keys.
    375         // clients will receive a call back to request certificates.
    376         if (!client) {
    377             for (String keyType : NativeCrypto.KEY_TYPES) {
    378                 try {
    379                     setCertificate(sslParameters.getKeyManager().chooseServerAlias(keyType,
    380                                                                                    null,
    381                                                                                    this));
    382                 } catch (CertificateEncodingException e) {
    383                     throw new IOException(e);
    384                 }
    385             }
    386         }
    387 
    388         NativeCrypto.setEnabledProtocols(sslNativePointer, enabledProtocols);
    389         NativeCrypto.setEnabledCipherSuites(sslNativePointer, enabledCipherSuites);
    390         if (enabledCompressionMethods.length != 0) {
    391             NativeCrypto.setEnabledCompressionMethods(sslNativePointer, enabledCompressionMethods);
    392         }
    393         if (useSessionTickets) {
    394             NativeCrypto.SSL_clear_options(sslNativePointer, NativeCrypto.SSL_OP_NO_TICKET);
    395         }
    396         if (hostname != null) {
    397             NativeCrypto.SSL_set_tlsext_host_name(sslNativePointer, hostname);
    398         }
    399 
    400         boolean enableSessionCreation = sslParameters.getEnableSessionCreation();
    401         if (!enableSessionCreation) {
    402             NativeCrypto.SSL_set_session_creation_enabled(sslNativePointer,
    403                                                           enableSessionCreation);
    404         }
    405 
    406         AbstractSessionContext sessionContext;
    407         OpenSSLSessionImpl session;
    408         if (client) {
    409             // look for client session to reuse
    410             ClientSessionContext clientSessionContext = sslParameters.getClientSessionContext();
    411             sessionContext = clientSessionContext;
    412             session = getCachedClientSession(clientSessionContext);
    413             if (session != null) {
    414                 NativeCrypto.SSL_set_session(sslNativePointer,  session.sslSessionNativePointer);
    415             }
    416         } else {
    417             sessionContext = sslParameters.getServerSessionContext();
    418             session = null;
    419         }
    420 
    421         // setup peer certificate verification
    422         if (client) {
    423             // TODO support for anonymous cipher would require us to
    424             // conditionally use SSL_VERIFY_NONE
    425         } else {
    426             // needing client auth takes priority...
    427             boolean certRequested = false;
    428             if (sslParameters.getNeedClientAuth()) {
    429                 NativeCrypto.SSL_set_verify(sslNativePointer,
    430                                             NativeCrypto.SSL_VERIFY_PEER
    431                                             | NativeCrypto.SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
    432                 certRequested = true;
    433             // ... over just wanting it...
    434             } else if (sslParameters.getWantClientAuth()) {
    435                 NativeCrypto.SSL_set_verify(sslNativePointer,
    436                                             NativeCrypto.SSL_VERIFY_PEER);
    437                 certRequested = true;
    438             // ... and it defaults properly so we don't need call SSL_set_verify in the common case.
    439             } else {
    440                 certRequested = false;
    441             }
    442 
    443             if (certRequested) {
    444                 X509Certificate[] issuers = sslParameters.getTrustManager().getAcceptedIssuers();
    445                 if (issuers != null && issuers.length != 0) {
    446                     byte[][] issuersBytes;
    447                     try {
    448                         issuersBytes = NativeCrypto.encodeIssuerX509Principals(issuers);
    449                     } catch (CertificateEncodingException e) {
    450                         throw new IOException("Problem encoding principals", e);
    451                     }
    452                     NativeCrypto.SSL_set_client_CA_list(sslNativePointer, issuersBytes);
    453                 }
    454             }
    455         }
    456 
    457         if (client && full) {
    458             // we want to do a full synchronous handshake, so turn off cutthrough
    459             NativeCrypto.SSL_clear_mode(sslNativePointer,
    460                                         NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH);
    461         }
    462 
    463         // BEGIN android-added
    464         // Temporarily use a different timeout for the handshake process
    465         int savedTimeoutMilliseconds = getSoTimeout();
    466         if (handshakeTimeoutMilliseconds >= 0) {
    467             setSoTimeout(handshakeTimeoutMilliseconds);
    468         }
    469         // END android-added
    470 
    471 
    472         int sslSessionNativePointer;
    473         try {
    474             sslSessionNativePointer = NativeCrypto.SSL_do_handshake(sslNativePointer, fd, this,
    475                                                                     getSoTimeout(), client);
    476         } catch (CertificateException e) {
    477             SSLHandshakeException exception = new SSLHandshakeException(e.getMessage());
    478             exception.initCause(e);
    479             throw exception;
    480         }
    481         byte[] sessionId = NativeCrypto.SSL_SESSION_session_id(sslSessionNativePointer);
    482         sslSession = (OpenSSLSessionImpl) sessionContext.getSession(sessionId);
    483         if (sslSession != null) {
    484             sslSession.lastAccessedTime = System.currentTimeMillis();
    485             LoggerHolder.logger.fine("Reused cached session for "
    486                                      + getInetAddress() + ".");
    487             NativeCrypto.SSL_SESSION_free(sslSessionNativePointer);
    488         } else {
    489             if (!enableSessionCreation) {
    490                 // Should have been prevented by NativeCrypto.SSL_set_session_creation_enabled
    491                 throw new IllegalStateException("SSL Session may not be created");
    492             }
    493             X509Certificate[] localCertificates
    494                     = createCertChain(NativeCrypto.SSL_get_certificate(sslNativePointer));
    495             X509Certificate[] peerCertificates
    496                     = createCertChain(NativeCrypto.SSL_get_peer_cert_chain(sslNativePointer));
    497             if (wrappedHost == null) {
    498                 sslSession = new OpenSSLSessionImpl(sslSessionNativePointer,
    499                                                     localCertificates, peerCertificates,
    500                                                     super.getInetAddress().getHostName(),
    501                                                     super.getPort(), sessionContext);
    502             } else  {
    503                 sslSession = new OpenSSLSessionImpl(sslSessionNativePointer,
    504                                                     localCertificates, peerCertificates,
    505                                                     wrappedHost, wrappedPort,
    506                                                     sessionContext);
    507             }
    508             // if not, putSession later in handshakeCompleted() callback
    509             if (handshakeCompleted) {
    510                 sessionContext.putSession(sslSession);
    511             }
    512             LoggerHolder.logger.fine("Created new session for "
    513                                      + getInetAddress().getHostName() + ".");
    514         }
    515 
    516         // BEGIN android-added
    517         // Restore the original timeout now that the handshake is complete
    518         if (handshakeTimeoutMilliseconds >= 0) {
    519             setSoTimeout(savedTimeoutMilliseconds);
    520         }
    521         // END android-added
    522 
    523         // if not, notifyHandshakeCompletedListeners later in handshakeCompleted() callback
    524         if (handshakeCompleted) {
    525             notifyHandshakeCompletedListeners();
    526         }
    527     }
    528 
    529     /**
    530      * Return a possibly null array of X509Certificates given the
    531      * possibly null array of DER encoded bytes.
    532      */
    533     private static X509Certificate[] createCertChain(byte[][] certificatesBytes) {
    534         if (certificatesBytes == null) {
    535             return null;
    536         }
    537         X509Certificate[] certificates = new X509Certificate[certificatesBytes.length];
    538         for (int i = 0; i < certificatesBytes.length; i++) {
    539             try {
    540                 certificates[i] = new X509CertImpl(certificatesBytes[i]);
    541             } catch (IOException e) {
    542                 return null;
    543             }
    544         }
    545         return certificates;
    546     }
    547 
    548     private void setCertificate(String alias) throws CertificateEncodingException, SSLException {
    549         if (alias == null) {
    550             return;
    551         }
    552 
    553         PrivateKey privateKey = sslParameters.getKeyManager().getPrivateKey(alias);
    554         byte[] privateKeyBytes = privateKey.getEncoded();
    555         NativeCrypto.SSL_use_PrivateKey(sslNativePointer, privateKeyBytes);
    556 
    557         X509Certificate[] certificates = sslParameters.getKeyManager().getCertificateChain(alias);
    558         byte[][] certificateBytes = NativeCrypto.encodeCertificates(certificates);
    559         NativeCrypto.SSL_use_certificate(sslNativePointer, certificateBytes);
    560 
    561         // checks the last installed private key and certificate,
    562         // so need to do this once per loop iteration
    563         NativeCrypto.SSL_check_private_key(sslNativePointer);
    564     }
    565 
    566     /**
    567      * Implementation of NativeCrypto.SSLHandshakeCallbacks
    568      * invoked via JNI from client_cert_cb
    569      */
    570     public void clientCertificateRequested(byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals)
    571             throws CertificateEncodingException, SSLException {
    572 
    573         String[] keyTypes = new String[keyTypeBytes.length];
    574         for (int i = 0; i < keyTypeBytes.length; i++) {
    575             keyTypes[i] = NativeCrypto.keyType(keyTypeBytes[i]);
    576         }
    577 
    578         X500Principal[] issuers;
    579         if (asn1DerEncodedPrincipals == null) {
    580             issuers = null;
    581         } else {
    582             issuers = new X500Principal[asn1DerEncodedPrincipals.length];
    583             for (int i = 0; i < asn1DerEncodedPrincipals.length; i++) {
    584                 issuers[i] = new X500Principal(asn1DerEncodedPrincipals[i]);
    585             }
    586         }
    587         setCertificate(sslParameters.getKeyManager().chooseClientAlias(keyTypes, issuers, this));
    588     }
    589 
    590     /**
    591      * Implementation of NativeCrypto.SSLHandshakeCallbacks
    592      * invoked via JNI from info_callback
    593      */
    594     public void handshakeCompleted() {
    595         handshakeCompleted = true;
    596 
    597         // If sslSession is null, the handshake was completed during
    598         // the call to NativeCrypto.SSL_do_handshake and not during a
    599         // later read operation. That means we do not need to fixup
    600         // the SSLSession and session cache or notify
    601         // HandshakeCompletedListeners, it will be done in
    602         // startHandshake.
    603         if (sslSession == null) {
    604             return;
    605         }
    606 
    607         // reset session id from the native pointer and update the
    608         // appropriate cache.
    609         sslSession.resetId();
    610         AbstractSessionContext sessionContext =
    611             (sslParameters.getUseClientMode())
    612             ? sslParameters.getClientSessionContext()
    613                 : sslParameters.getServerSessionContext();
    614         sessionContext.putSession(sslSession);
    615 
    616         // let listeners know we are finally done
    617         notifyHandshakeCompletedListeners();
    618     }
    619 
    620     private void notifyHandshakeCompletedListeners() {
    621         if (listeners != null && !listeners.isEmpty()) {
    622             // notify the listeners
    623             HandshakeCompletedEvent event =
    624                 new HandshakeCompletedEvent(this, sslSession);
    625             for (HandshakeCompletedListener listener : listeners) {
    626                 try {
    627                     listener.handshakeCompleted(event);
    628                 } catch (RuntimeException e) {
    629                     // The RI runs the handlers in a separate thread,
    630                     // which we do not. But we try to preserve their
    631                     // behavior of logging a problem and not killing
    632                     // the handshaking thread just because a listener
    633                     // has a problem.
    634                     Thread thread = Thread.currentThread();
    635                     thread.getUncaughtExceptionHandler().uncaughtException(thread, e);
    636                 }
    637             }
    638         }
    639     }
    640 
    641     /**
    642      * Implementation of NativeCrypto.SSLHandshakeCallbacks
    643      *
    644      * @param bytes An array of ASN.1 DER encoded certficates
    645      * @param authMethod auth algorithm name
    646      *
    647      * @throws CertificateException if the certificate is untrusted
    648      */
    649     @SuppressWarnings("unused")
    650     public void verifyCertificateChain(byte[][] bytes, String authMethod)
    651             throws CertificateException {
    652         try {
    653             if (bytes == null || bytes.length == 0) {
    654                 throw new SSLException("Peer sent no certificate");
    655             }
    656             X509Certificate[] peerCertificateChain = new X509Certificate[bytes.length];
    657             for (int i = 0; i < bytes.length; i++) {
    658                 peerCertificateChain[i] =
    659                     new X509CertImpl(
    660                         javax.security.cert.X509Certificate.getInstance(bytes[i]).getEncoded());
    661             }
    662             boolean client = sslParameters.getUseClientMode();
    663             if (client) {
    664                 sslParameters.getTrustManager().checkServerTrusted(peerCertificateChain,
    665                                                                    authMethod);
    666             } else {
    667                 sslParameters.getTrustManager().checkClientTrusted(peerCertificateChain,
    668                                                                    authMethod);
    669             }
    670 
    671         } catch (CertificateException e) {
    672             throw e;
    673         } catch (Exception e) {
    674             throw new RuntimeException(e);
    675         }
    676     }
    677 
    678     /**
    679      * Returns an input stream for this SSL socket using native calls to the
    680      * OpenSSL library.
    681      *
    682      * @return: an input stream for reading bytes from this socket.
    683      * @throws: <code>IOException</code> if an I/O error occurs when creating
    684      *          the input stream, the socket is closed, the socket is not
    685      *          connected, or the socket input has been shutdown.
    686      */
    687     @Override
    688     public InputStream getInputStream() throws IOException {
    689         checkOpen();
    690         synchronized (this) {
    691             if (is == null) {
    692                 is = new SSLInputStream();
    693             }
    694 
    695             return is;
    696         }
    697     }
    698 
    699     /**
    700      * Returns an output stream for this SSL socket using native calls to the
    701      * OpenSSL library.
    702      *
    703      * @return an output stream for writing bytes to this socket.
    704      * @throws <code>IOException</code> if an I/O error occurs when creating
    705      *             the output stream, or no connection to the socket exists.
    706      */
    707     @Override
    708     public OutputStream getOutputStream() throws IOException {
    709         checkOpen();
    710         synchronized (this) {
    711             if (os == null) {
    712                 os = new SSLOutputStream();
    713             }
    714 
    715             return os;
    716         }
    717     }
    718 
    719     /**
    720      * This method is not supported for this SSLSocket implementation
    721      * because reading from an SSLSocket may involve writing to the
    722      * network.
    723      */
    724     @Override
    725     public void shutdownInput() throws IOException {
    726         throw new UnsupportedOperationException();
    727     }
    728 
    729     /**
    730      * This method is not supported for this SSLSocket implementation
    731      * because writing to an SSLSocket may involve reading from the
    732      * network.
    733      */
    734     @Override
    735     public void shutdownOutput() throws IOException {
    736         throw new UnsupportedOperationException();
    737     }
    738 
    739     /**
    740      * This inner class provides input data stream functionality
    741      * for the OpenSSL native implementation. It is used to
    742      * read data received via SSL protocol.
    743      */
    744     private class SSLInputStream extends InputStream {
    745         SSLInputStream() throws IOException {
    746             /**
    747             /* Note: When startHandshake() throws an exception, no
    748              * SSLInputStream object will be created.
    749              */
    750             OpenSSLSocketImpl.this.startHandshake(false);
    751         }
    752 
    753         /**
    754          * Reads one byte. If there is no data in the underlying buffer,
    755          * this operation can block until the data will be
    756          * available.
    757          * @return read value.
    758          * @throws <code>IOException</code>
    759          */
    760         @Override
    761         public int read() throws IOException {
    762             BlockGuard.getThreadPolicy().onNetwork();
    763             synchronized (readLock) {
    764                 checkOpen();
    765                 return NativeCrypto.SSL_read_byte(sslNativePointer, fd, OpenSSLSocketImpl.this,
    766                                                   getSoTimeout());
    767             }
    768         }
    769 
    770         /**
    771          * Method acts as described in spec for superclass.
    772          * @see java.io.InputStream#read(byte[],int,int)
    773          */
    774         @Override
    775         public int read(byte[] b, int off, int len) throws IOException {
    776             BlockGuard.getThreadPolicy().onNetwork();
    777             synchronized (readLock) {
    778                 checkOpen();
    779                 if (b == null) {
    780                     throw new NullPointerException("b == null");
    781                 }
    782                 if ((len | off) < 0 || len > b.length - off) {
    783                     throw new IndexOutOfBoundsException();
    784                 }
    785                 if (0 == len) {
    786                     return 0;
    787                 }
    788                 return NativeCrypto.SSL_read(sslNativePointer, fd, OpenSSLSocketImpl.this,
    789                                              b, off, len, getSoTimeout());
    790             }
    791         }
    792     }
    793 
    794     /**
    795      * This inner class provides output data stream functionality
    796      * for the OpenSSL native implementation. It is used to
    797      * write data according to the encryption parameters given in SSL context.
    798      */
    799     private class SSLOutputStream extends OutputStream {
    800         SSLOutputStream() throws IOException {
    801             /**
    802             /* Note: When startHandshake() throws an exception, no
    803              * SSLOutputStream object will be created.
    804              */
    805             OpenSSLSocketImpl.this.startHandshake(false);
    806         }
    807 
    808         /**
    809          * Method acts as described in spec for superclass.
    810          * @see java.io.OutputStream#write(int)
    811          */
    812         @Override
    813         public void write(int b) throws IOException {
    814             BlockGuard.getThreadPolicy().onNetwork();
    815             synchronized (writeLock) {
    816                 checkOpen();
    817                 NativeCrypto.SSL_write_byte(sslNativePointer, fd, OpenSSLSocketImpl.this, b);
    818             }
    819         }
    820 
    821         /**
    822          * Method acts as described in spec for superclass.
    823          * @see java.io.OutputStream#write(byte[],int,int)
    824          */
    825         @Override
    826         public void write(byte[] b, int start, int len) throws IOException {
    827             BlockGuard.getThreadPolicy().onNetwork();
    828             synchronized (writeLock) {
    829                 checkOpen();
    830                 if (b == null) {
    831                     throw new NullPointerException("b == null");
    832                 }
    833                 if ((len | start) < 0 || len > b.length - start) {
    834                     throw new IndexOutOfBoundsException();
    835                 }
    836                 if (len == 0) {
    837                     return;
    838                 }
    839                 NativeCrypto.SSL_write(sslNativePointer, fd, OpenSSLSocketImpl.this, b, start, len);
    840             }
    841         }
    842     }
    843 
    844 
    845     /**
    846      * The SSL session used by this connection is returned. The SSL session
    847      * determines which cipher suite should be used by all connections within
    848      * that session and which identities have the session's client and server.
    849      * This method starts the SSL handshake.
    850      * @return the SSLSession.
    851      * @throws <code>IOException</code> if the handshake fails
    852      */
    853     @Override
    854     public SSLSession getSession() {
    855         if (sslSession == null) {
    856             try {
    857                 startHandshake(true);
    858             } catch (IOException e) {
    859 
    860                 // return an invalid session with
    861                 // invalid cipher suite of "SSL_NULL_WITH_NULL_NULL"
    862                 return SSLSessionImpl.NULL_SESSION;
    863             }
    864         }
    865         return sslSession;
    866     }
    867 
    868     /**
    869      * Registers a listener to be notified that a SSL handshake
    870      * was successfully completed on this connection.
    871      * @throws <code>IllegalArgumentException</code> if listener is null.
    872      */
    873     @Override
    874     public void addHandshakeCompletedListener(
    875             HandshakeCompletedListener listener) {
    876         if (listener == null) {
    877             throw new IllegalArgumentException("Provided listener is null");
    878         }
    879         if (listeners == null) {
    880             listeners = new ArrayList();
    881         }
    882         listeners.add(listener);
    883     }
    884 
    885     /**
    886      * The method removes a registered listener.
    887      * @throws IllegalArgumentException if listener is null or not registered
    888      */
    889     @Override
    890     public void removeHandshakeCompletedListener(
    891             HandshakeCompletedListener listener) {
    892         if (listener == null) {
    893             throw new IllegalArgumentException("Provided listener is null");
    894         }
    895         if (listeners == null) {
    896             throw new IllegalArgumentException(
    897                     "Provided listener is not registered");
    898         }
    899         if (!listeners.remove(listener)) {
    900             throw new IllegalArgumentException(
    901                     "Provided listener is not registered");
    902         }
    903     }
    904 
    905     /**
    906      * Returns true if new SSL sessions may be established by this socket.
    907      *
    908      * @return true if the session may be created; false if a session already
    909      *         exists and must be resumed.
    910      */
    911     @Override
    912     public boolean getEnableSessionCreation() {
    913         return sslParameters.getEnableSessionCreation();
    914     }
    915 
    916     /**
    917      * Set a flag for the socket to inhibit or to allow the creation of a new
    918      * SSL sessions. If the flag is set to false, and there are no actual
    919      * sessions to resume, then there will be no successful handshaking.
    920      *
    921      * @param flag true if session may be created; false
    922      *            if a session already exists and must be resumed.
    923      */
    924     @Override
    925     public void setEnableSessionCreation(boolean flag) {
    926         sslParameters.setEnableSessionCreation(flag);
    927     }
    928 
    929     /**
    930      * The names of the cipher suites which could be used by the SSL connection
    931      * are returned.
    932      * @return an array of cipher suite names
    933      */
    934     @Override
    935     public String[] getSupportedCipherSuites() {
    936         return NativeCrypto.getSupportedCipherSuites();
    937     }
    938 
    939     /**
    940      * The names of the cipher suites that are in use in the actual the SSL
    941      * connection are returned.
    942      *
    943      * @return an array of cipher suite names
    944      */
    945     @Override
    946     public String[] getEnabledCipherSuites() {
    947         return enabledCipherSuites.clone();
    948     }
    949 
    950     /**
    951      * This method enables the cipher suites listed by
    952      * getSupportedCipherSuites().
    953      *
    954      * @param suites names of all the cipher suites to
    955      *            put on use
    956      * @throws IllegalArgumentException when one or more of the
    957      *             ciphers in array suites are not supported, or when the array
    958      *             is null.
    959      */
    960     @Override
    961     public void setEnabledCipherSuites(String[] suites) {
    962         enabledCipherSuites = NativeCrypto.checkEnabledCipherSuites(suites);
    963     }
    964 
    965     /**
    966      * The names of the protocols' versions that may be used on this SSL
    967      * connection.
    968      * @return an array of protocols names
    969      */
    970     @Override
    971     public String[] getSupportedProtocols() {
    972         return NativeCrypto.getSupportedProtocols();
    973     }
    974 
    975     /**
    976      * The names of the protocols' versions that are in use on this SSL
    977      * connection.
    978      *
    979      * @return an array of protocols names
    980      */
    981     @Override
    982     public String[] getEnabledProtocols() {
    983         return enabledProtocols.clone();
    984     }
    985 
    986     /**
    987      * This method enables the protocols' versions listed by
    988      * getSupportedProtocols().
    989      *
    990      * @param protocols The names of all the protocols to allow
    991      *
    992      * @throws IllegalArgumentException when one or more of the names in the
    993      *             array are not supported, or when the array is null.
    994      */
    995     @Override
    996     public void setEnabledProtocols(String[] protocols) {
    997         enabledProtocols = NativeCrypto.checkEnabledProtocols(protocols);
    998     }
    999 
   1000     /**
   1001      * The names of the compression methods that may be used on this SSL
   1002      * connection.
   1003      * @return an array of compression methods
   1004      */
   1005     public String[] getSupportedCompressionMethods() {
   1006         return NativeCrypto.getSupportedCompressionMethods();
   1007     }
   1008 
   1009     /**
   1010      * The names of the compression methods versions that are in use
   1011      * on this SSL connection.
   1012      *
   1013      * @return an array of compression methods
   1014      */
   1015     public String[] getEnabledCompressionMethods() {
   1016         return enabledCompressionMethods.clone();
   1017     }
   1018 
   1019     /**
   1020      * This method enables the compression method listed by
   1021      * getSupportedCompressionMethods().
   1022      *
   1023      * @param methods The names of all the compression methods to allow
   1024      *
   1025      * @throws IllegalArgumentException when one or more of the names in the
   1026      *             array are not supported, or when the array is null.
   1027      */
   1028     public void setEnabledCompressionMethods (String[] methods) {
   1029         enabledCompressionMethods = NativeCrypto.checkEnabledCompressionMethods(methods);
   1030     }
   1031 
   1032     /**
   1033      * This method enables session ticket support.
   1034      *
   1035      * @param useSessionTickets True to enable session tickets
   1036      */
   1037     public void setUseSessionTickets(boolean useSessionTickets) {
   1038         this.useSessionTickets = useSessionTickets;
   1039     }
   1040 
   1041     /**
   1042      * This method gives true back if the SSL socket is set to client mode.
   1043      *
   1044      * @return true if the socket should do the handshaking as client.
   1045      */
   1046     public boolean getUseSessionTickets() {
   1047         return useSessionTickets;
   1048     }
   1049 
   1050     /**
   1051      * This method enables Server Name Indication
   1052      *
   1053      * @param hostname the desired SNI hostname, or null to disable
   1054      */
   1055     public void setHostname(String hostname) {
   1056         this.hostname = hostname;
   1057     }
   1058 
   1059     /**
   1060      * This method returns the current SNI hostname
   1061      *
   1062      * @return a host name if SNI is enabled, or null otherwise
   1063      */
   1064     public String getHostname() {
   1065         return hostname;
   1066     }
   1067 
   1068     /**
   1069      * This method gives true back if the SSL socket is set to client mode.
   1070      *
   1071      * @return true if the socket should do the handshaking as client.
   1072      */
   1073     public boolean getUseClientMode() {
   1074         return sslParameters.getUseClientMode();
   1075     }
   1076 
   1077     /**
   1078      * This method set the actual SSL socket to client mode.
   1079      *
   1080      * @param mode true if the socket starts in client
   1081      *            mode
   1082      * @throws IllegalArgumentException if mode changes during
   1083      *             handshake.
   1084      */
   1085     @Override
   1086     public void setUseClientMode(boolean mode) {
   1087         if (handshakeStarted) {
   1088             throw new IllegalArgumentException(
   1089             "Could not change the mode after the initial handshake has begun.");
   1090         }
   1091         sslParameters.setUseClientMode(mode);
   1092     }
   1093 
   1094     /**
   1095      * Returns true if the SSL socket requests client's authentication. Relevant
   1096      * only for server sockets!
   1097      *
   1098      * @return true if client authentication is desired, false if not.
   1099      */
   1100     @Override
   1101     public boolean getWantClientAuth() {
   1102         return sslParameters.getWantClientAuth();
   1103     }
   1104 
   1105     /**
   1106      * Returns true if the SSL socket needs client's authentication. Relevant
   1107      * only for server sockets!
   1108      *
   1109      * @return true if client authentication is desired, false if not.
   1110      */
   1111     @Override
   1112     public boolean getNeedClientAuth() {
   1113         return sslParameters.getNeedClientAuth();
   1114     }
   1115 
   1116     /**
   1117      * Sets the SSL socket to use client's authentication. Relevant only for
   1118      * server sockets!
   1119      *
   1120      * @param need true if client authentication is
   1121      *            desired, false if not.
   1122      */
   1123     @Override
   1124     public void setNeedClientAuth(boolean need) {
   1125         sslParameters.setNeedClientAuth(need);
   1126     }
   1127 
   1128     /**
   1129      * Sets the SSL socket to use client's authentication. Relevant only for
   1130      * server sockets! Notice that in contrast to setNeedClientAuth(..) this
   1131      * method will continue the negotiation if the client decide not to send
   1132      * authentication credentials.
   1133      *
   1134      * @param want true if client authentication is
   1135      *            desired, false if not.
   1136      */
   1137     @Override
   1138     public void setWantClientAuth(boolean want) {
   1139         sslParameters.setWantClientAuth(want);
   1140     }
   1141 
   1142     /**
   1143      * This method is not supported for SSLSocket implementation.
   1144      */
   1145     @Override
   1146     public void sendUrgentData(int data) throws IOException {
   1147         throw new SocketException(
   1148                 "Method sendUrgentData() is not supported.");
   1149     }
   1150 
   1151     /**
   1152      * This method is not supported for SSLSocket implementation.
   1153      */
   1154     @Override
   1155     public void setOOBInline(boolean on) throws SocketException {
   1156         throw new SocketException(
   1157                 "Methods sendUrgentData, setOOBInline are not supported.");
   1158     }
   1159 
   1160     /**
   1161      * Set the read timeout on this socket. The SO_TIMEOUT option, is specified
   1162      * in milliseconds. The read operation will block indefinitely for a zero
   1163      * value.
   1164      *
   1165      * @param timeout the read timeout value
   1166      * @throws SocketException if an error occurs setting the option
   1167      */
   1168     @Override
   1169     public void setSoTimeout(int timeoutMilliseconds) throws SocketException {
   1170         super.setSoTimeout(timeoutMilliseconds);
   1171         this.timeoutMilliseconds = timeoutMilliseconds;
   1172     }
   1173 
   1174     @Override
   1175     public int getSoTimeout() throws SocketException {
   1176         return timeoutMilliseconds;
   1177     }
   1178 
   1179     // BEGIN android-added
   1180     /**
   1181      * Set the handshake timeout on this socket.  This timeout is specified in
   1182      * milliseconds and will be used only during the handshake process.
   1183      *
   1184      * @param timeout the handshake timeout value
   1185      */
   1186     public void setHandshakeTimeout(int timeoutMilliseconds) throws SocketException {
   1187         this.handshakeTimeoutMilliseconds = timeoutMilliseconds;
   1188     }
   1189     // END android-added
   1190 
   1191     /**
   1192      * Closes the SSL socket. Once closed, a socket is not available for further
   1193      * use anymore under any circumstance. A new socket must be created.
   1194      *
   1195      * @throws <code>IOException</code> if an I/O error happens during the
   1196      *             socket's closure.
   1197      */
   1198     @Override
   1199     public void close() throws IOException {
   1200         // TODO: Close SSL sockets using a background thread so they close
   1201         // gracefully.
   1202 
   1203         synchronized (handshakeLock) {
   1204             if (!handshakeStarted) {
   1205                 // prevent further attemps to start handshake
   1206                 handshakeStarted = true;
   1207 
   1208                 synchronized (this) {
   1209                     free();
   1210 
   1211                     if (socket != this) {
   1212                         if (autoClose && !socket.isClosed()) socket.close();
   1213                     } else {
   1214                         if (!super.isClosed()) super.close();
   1215                     }
   1216                 }
   1217 
   1218                 return;
   1219             }
   1220         }
   1221 
   1222         NativeCrypto.SSL_interrupt(sslNativePointer);
   1223 
   1224         synchronized (this) {
   1225             synchronized (writeLock) {
   1226                 synchronized (readLock) {
   1227 
   1228                     // Shut down the SSL connection, per se.
   1229                     try {
   1230                         if (handshakeStarted) {
   1231                             BlockGuard.getThreadPolicy().onNetwork();
   1232                             NativeCrypto.SSL_shutdown(sslNativePointer, fd, this);
   1233                         }
   1234                     } catch (IOException ignored) {
   1235                         /*
   1236                          * Note that although close() can throw
   1237                          * IOException, the RI does not throw if there
   1238                          * is problem sending a "close notify" which
   1239                          * can happen if the underlying socket is closed.
   1240                          */
   1241                     }
   1242 
   1243                     /*
   1244                      * Even if the above call failed, it is still safe to free
   1245                      * the native structs, and we need to do so lest we leak
   1246                      * memory.
   1247                      */
   1248                     free();
   1249 
   1250                     if (socket != this) {
   1251                         if (autoClose && !socket.isClosed())
   1252                             socket.close();
   1253                     } else {
   1254                         if (!super.isClosed())
   1255                             super.close();
   1256                     }
   1257                 }
   1258             }
   1259         }
   1260     }
   1261 
   1262     private void free() {
   1263         if (sslNativePointer == 0) {
   1264             return;
   1265         }
   1266         NativeCrypto.SSL_free(sslNativePointer);
   1267         sslNativePointer = 0;
   1268     }
   1269 
   1270     @Override protected void finalize() throws Throwable {
   1271         try {
   1272             /*
   1273              * Just worry about our own state. Notably we do not try and
   1274              * close anything. The SocketImpl, either our own
   1275              * PlainSocketImpl, or the Socket we are wrapping, will do
   1276              * that. This might mean we do not properly SSL_shutdown, but
   1277              * if you want to do that, properly close the socket yourself.
   1278              *
   1279              * The reason why we don't try to SSL_shutdown, is that there
   1280              * can be a race between finalizers where the PlainSocketImpl
   1281              * finalizer runs first and closes the socket. However, in the
   1282              * meanwhile, the underlying file descriptor could be reused
   1283              * for another purpose. If we call SSL_shutdown, the
   1284              * underlying socket BIOs still have the old file descriptor
   1285              * and will write the close notify to some unsuspecting
   1286              * reader.
   1287              */
   1288             updateInstanceCount(-1);
   1289             free();
   1290         } finally {
   1291             super.finalize();
   1292         }
   1293     }
   1294 }
   1295