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